Операторы ?:, !! и умные приведения
Учимся подставлять значения по умолчанию и понимать, когда !! опасен.
Элвис-оператор
?:возвращает левое значение, если оно неnull, иначе — правое (значение по умолчанию).
Оператор ?. мы освоили, но что делать, когда вместо null нужно подставить конкретное значение? Здесь на сцену выходит элвис-оператор и его опасный сосед — !!.
Элвис-оператор ?:
Название шуточное: символы ?: напоминают причёску и глаза Элвиса. Оператор задаёт запасное значение на случай null.
fun main() {
val name: String? = null
val display = name ?: "Гость"
println(display)
val len = name?.length ?: 0
println(len)
}Вывод:
Гость 0
Оператор !! — на свой страх и риск
Оператор !! говорит компилятору: «я уверен, что тут не null». Если вы ошиблись и значение всё-таки null, программа упадёт с NullPointerException. Это фактически отказ от защиты, поэтому используют его редко.
fun main() {
val name: String? = "Аня"
println(name!!.length) // сработает: name не null
// если бы name был null — NullPointerException
}Вывод:
3
Умное приведение типов (smart cast)
После явной проверки на null компилятор сам «понимает», что внутри ветки значение точно не null, и разрешает обращаться к нему как к обычному типу — без ?..
fun describe(text: String?) {
if (text != null) {
// здесь text уже String, не String?
println("Длина: ${text.length}")
} else {
println("Пусто")
}
}
fun main() {
describe("Котлин")
describe(null)
}Вывод:
Длина: 6 Пусто
Как работает под капотом
Smart cast возможен, когда компилятор может гарантировать, что между проверкой и использованием значение не изменилось. Для val это всегда так. А вот для изменяемого var (особенно свойства класса) smart cast может не сработать: значение теоретически могло поменяться в другом потоке. В таких случаях значение сохраняют в локальный val.
Частые ошибки
- Злоупотреблять
!!. Каждый!!— потенциальныйNullPointerException; почти всегда лучше?:или проверка. - Дублировать значение по умолчанию.
name ?: "Гость"короче и яснее, чемif. - Ждать smart cast для
var-свойства. Сохраните его в локальнуюval-переменную.
Итог
?:подставляет значение по умолчанию вместоnull.!!отключает защиту и кидаетNullPointerExceptionприnull— применяйте осторожно.- После проверки
if (x != null)срабатывает smart cast. - Для надёжного smart cast используйте
val.