Как Kotlin решает проблему NullPointerException и что происходит на границе с Java-кодом?
Короткий ответ
- Nullable и non-null типы различаются на уровне системы типов: String и String?
- К nullable нельзя обратиться без проверки — компилятор не даст
- Операторы: ?. безопасный вызов, ?: элвис, !! осознанный риск
- let, smart cast после проверки if (x != null)
- Типы из Java без аннотаций — платформенные (String!), проверки нет
- Аннотации @Nullable/@NotNull в Java-коде делают границу безопасной
Kotlin выносит nullability в систему типов, а риски остаются на границе с Java через платформенные типы.
Как сказать вслух
пример ответаВ Котлине возможность null — часть типа: строка без знака вопроса не может быть null в принципе, а со знаком — может, и компилятор заставит это обработать. Для работы с nullable есть безопасный вызов через ?., элвис-оператор для значения по умолчанию, и !!, который бросит исключение, если там всё-таки null. Опасное место — вызовы Java-кода: там типы платформенные, компилятор ничего не гарантирует, поэтому в Java-коде стоит расставлять аннотации nullability.
Подробный ответ
Основной ответ
Система типов Kotlin разделяет T и T?: присвоить null в String нельзя, а к String? нельзя обратиться без обработки. Инструменты: safe call a?.b (вернёт null, если a == null), элвис a ?: default, безопасное приведение as?, функции области видимости (let для «если не null»), smart cast — после проверки if (a != null) компилятор сам сужает тип. Оператор !! принудительно разыменовывает и бросает NullPointerException — его используют осознанно и редко. Коллекции тоже типизированы: List<String> vs List<String?>. Граница с Java: типы без аннотаций приходят как платформенные (обозначаются String!), и компилятор доверяет разработчику — NPE возможен в рантайме. Аннотации JSR-305/JetBrains (@Nullable, @NotNull) в Java-коде превращают платформенные типы в честные nullable/non-null. lateinit var — для внедрения зависимостей, бросает понятную ошибку при раннем доступе.
Ключевые моменты
- Типы, а не проверки. Nullability — часть сигнатуры; ошибка ловится компиляцией, а не в проде.
- Операторы. ?. и ?: покрывают большинство случаев; !! — красный флаг на код-ревью без обоснования.
- Платформенные типы. Java без аннотаций — зона без гарантий; NPE из Java-вызова остаётся возможным.
- Smart cast. Работает для val и локальных переменных; для изменяемых свойств нужен let или локальная копия.
Практический контекст
В смешанных Java/Kotlin-проектах (типичный бэкенд на Spring) главные NPE живут на границе: Java-метод вернул null там, где Kotlin ждал non-null. Поэтому команды аннотируют Java-API и включают строгий режим JSR-305. Интервьюер проверяет не перечисление операторов, а понимание платформенных типов и привычку избегать !!. Плюсом будет упомянуть, что data-классы и дефолтные значения параметров тоже снижают число null-полей.
Частые ошибки
- Злоупотребляют !! вместо нормальной обработки nullable
- Считают, что Kotlin полностью исключает NPE, забывая про Java-interop и lateinit
- Не знают, что smart cast не работает с изменяемыми свойствами класса