В чём разница между !! и ?.?
В Kotlin !! и ?. — это два оператора, предназначенных для работы с nullable-типами, но они противоположны по поведению и цели.
!! — Not-null assertion operator
Оператор !! говорит компилятору: «Я точно знаю, что это значение не null. Убери проверку». Если значение окажется null, произойдёт NullPointerException.
Синтаксис:
val name: String? = null
val length = name!!.length // Бросит NullPointerException
Где применяется:
-
Когда ты на 100% уверен, что переменная не null (возможно, по логике до этого момента).
-
Когда хочется быстро «заставить» nullable-значение вести себя как ненулевое (не рекомендуется без особой необходимости).
-
Иногда в тестах, прототипах, при переходе от Java.
Поведение:
-
null → ❌ выброс исключения.
-
ненулевое значение → ✅ работает как обычная переменная.
?. — Safe call operator
Оператор ?. используется для безопасного обращения к свойствам или методам объекта, который может быть null. Если значение не null, операция выполняется. Если значение null, результат — тоже null, и вызов не происходит.
Синтаксис:
val name: String? = null
val length = name?.length // length будет null, но без исключения
Где применяется:
-
Когда ты не уверен, есть ли значение, и хочешь избежать NullPointerException.
-
Для цепочки вызовов (?. можно ставить подряд).
-
Вместе с ?: (Elvis-оператором) — для установки значений по умолчанию.
Поведение:
-
null → возвращается null.
-
ненулевое значение → результат выражения.
Табличка сравнения
Оператор | Назначение | При null | При not null | Возвращает |
---|---|---|---|---|
!! | Принудительно обращаться как к not-null | NPE (ошибка) | Значение | Тот же тип без ? |
--- | --- | --- | --- | --- |
?. | Безопасно вызвать метод/свойство | null | Результат | Nullable-результат |
--- | --- | --- | --- | --- |
Примеры сравнения
Пример 1:
val name: String? = "Kotlin"
val upper = name!!.uppercase() // OK: "KOTLIN"
val name: String? = null
val upper = name!!.uppercase() // Бросит NullPointerException
Пример 2:
val name: String? = "Kotlin"
val upper = name?.uppercase() // OK: "KOTLIN"
val name: String? = null
val upper = name?.uppercase() // OK: null
Почему !! опасен
Использование !! — это потенциально опасное действие. Оно убирает защиту от null, которую Kotlin внедряет на уровне типов.
fun printLength(s: String?) {
println(s!!.length) // Если s == null — будет NPE
}
Такие ошибки могут проявиться в рантайме, а не на этапе компиляции.
Альтернатива !! — безопасные конструкции
С ?. и ?:
val name: String? = null
val length = name?.length ?: 0 // Безопасно, результат: 0
С let
name?.let {
println(it.length)
}
С if (name != null)
if (name != null) {
println(name.length)
}
Пример с ошибкой и альтернативой
❌ Ошибка:
val user: User? = getUser()
val city = user!!.address!!.city!!.name // Много !! → высокая вероятность NPE
✅ Безопасно:
val city = user?.address?.city?.name ?: "Unknown"
Оператор !! — это костыль, который лучше избегать. Оператор ?. — инструмент, встроенный в философию Kotlin, помогающий писать безопасный и предсказуемый код.