Проверка на null проходит, но smart cast не компилируется — почему и как это исправить?
render() проверяет свойство на null, но компилятор всё равно отвергает name.length с сообщением «smart cast to 'String' is impossible».
Ограничения: name должен остаться var-свойством класса (его переприсваивает другой код), не используйте !!, а render() обязан прочитать одно согласованное значение.
class Screen {
var name: String? = null
fun render(): Int {
if (name != null) {
return name.length // не компилируется
}
return 0
}
}
Найдите причину и исправьте ошибку.
Smart cast требует доказательства, что значение не изменится между проверкой и использованием. Изменяемое свойство может переприсвоить другой поток, поэтому в приведении отказано. Прочитайте его в локальную val; name?.length ?: 0 тоже годится.
- ✗Думать, что smart cast работает на любом
var, включая свойство класса - ✗Хвататься за
!!вместо копирования значения в локальнуюval - ✗Считать, что свойство с пользовательским getter можно smart-cast-ить
- →Почему свойство с пользовательским getter тоже не поддаётся smart cast?
- →Скомпилируется ли тот же код, будь
nameлокальнойvarвнутри функции?
Почему компилятор отказывает
Smart cast — это не проверка в рантайме, а вывод компилятора: после if (name != null) он готов считать тип String вместо String?, но только если может доказать, что между проверкой и использованием значение не изменится.
Для var-свойства класса такого доказательства нет: его может переприсвоить другой поток (или код, вызванный между строками). Для свойства с пользовательским getter — тоже: каждый вызов может вернуть новое значение. Поэтому оба случая отвергаются, а локальная val — принимается.
Исправление: локальная val
class Screen {
var name: String? = null
fun render(): Int {
val n = name // одно чтение, дальше значение неизменно
return if (n != null) n.length else 0
}
}
n — локальная val, её изменить некому, поэтому внутри if она сама приводится к String.
То же самое идиоматично
fun render(): Int = name?.length ?: 0
Safe call читает name один раз, ?: даёт запасное значение. !! здесь не нужен: он лишь превратил бы гонку в NullPointerException.