Упаковка и равенство
Kotlin показывает единый тип Int, но под капотом на JVM он живёт в двух формах: примитив int (лежит по значению, без аллокации) и объект-обёртка Integer (лежит в куче, доступен по ссылке). Переход из первой формы во вторую называется упаковкой (boxing). Она происходит незаметно — стоит числу попасть туда, где нужен объект: в Int?, в дженерик, в коллекцию.
Главная ловушка всплывает на сравнении. Оператор === сравнивает ссылки, == — содержимое. Для упакованных чисел === зависит от того, один ли это объект, а JVM кэширует маленькие Integer, поэтому === даёт true для 1 и false для 1000. Понимание этих переходов отделяет «выучил синтаксис» от «понимаю рантайм». Полная карта — в слоях ниже.
Карта темы
- Упаковка примитивов — когда
Intкомпилируется в примитивint, а когда в объектInteger. - Nullable-типы и упаковка — почему
Int?всегда упакован и чем отличается отInt. - Ссылочное vs структурное равенство — разница
===и==и когда упаковка делает===коварным. - Кэш Integer — почему
Integerдля-128..127кэшируется и как это меняет результат===.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Считать, что Int в Kotlin всегда объект | На деле non-nullable Int — примитив int, аллокации нет |
Думать, что Int? и Int одинаковы в рантайме | Int? упакован в Integer, Int — примитив; разное представление |
Сравнивать числа-объекты через === | Сравнение по ссылке — true/false зависит от кэша, а не от значения |
Обобщать результат === с маленьких чисел на большие | Кэш покрывает лишь -128..127; вне него два объекта не равны по === |
Забывать, что коллекция или дженерик упаковывают Int | Скрытые аллокации Integer в горячем коде |
Значение для собеседований
Упаковку спрашивают как проверку того, видите ли вы за красивым синтаксисом Kotlin реальное представление на JVM. Кандидат, который говорит «Int — примитив, Int? — упакованный Integer, а === сравнивает ссылки», сразу опережает того, кто уверен, что «в Kotlin всё объекты».
Что обычно проверяют:
- Когда
Intостаётся примитивом, а когда упаковывается. - Чем
Int?отличается отIntна уровне байткода. - Разницу
===(ссылка) и==(содержимое). - Почему
===для1даётtrue, а для1000—false.
Типичный неверный ответ: «в Kotlin нет примитивов, каждый Int — объект». На деле компилятор представляет non-nullable Int примитивом int; объектом он становится только при упаковке — в Int?, дженерике или коллекции.