Глиф Учебники

Ошибка Only safe (?.) or non-null asserted (!!.) calls are allowed

Справочник · Kotlin · 1 минута чтения

Компилятор пишет примерно так:

Текст
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Значит, переменная может оказаться null (её тип со знаком ? — здесь String?), а программа обращается к ней как к обычному значению: s.length, s.uppercase(). Kotlin не даёт собрать такой код, пока не ясно, что делать при null.

Как исправить

Kotlin
fun main() {
    val s: String? = readlnOrNull()

    // 1. Проверить на null
    if (s != null) {
        println(s.length)
    }

    // 2. Безопасный вызов: при null результат — null
    println(s?.length)

    // 3. Значение по умолчанию
    val len = s?.length ?: 0
    println(len)

    // 4. Выполнить блок, только если не null
    s?.let { println(it.uppercase()) }

    // 5. «Точно не null» — при null программа упадёт
    println(s!!.length)
}

Какой способ выбрать:

  1. if (s != null) — когда при null нужно сделать что-то своё. Внутри проверки компилятор считает s обычной строкой.
  2. s?.length — когда null в результате допустим.
  3. s?.length ?: 0 — когда нужно запасное значение. Справа от ?: можно поставить и return.
  4. s?.let { ... } — когда с непустым значением надо сделать несколько действий.
  5. s!!.length — только когда null действительно невозможен. При null — NullPointerException.

Подробно — в уроке Null-безопасность.

Частые причины

readLine() вместо readln(). Старая readLine() возвращает String?, поэтому readLine().toInt() не собирается. Возьмите readln() — она возвращает обычную String:

Kotlin
fun main() {
    val n = readln().toInt()
    println(n * 2)
}
Ввод
21
Вывод
42

Значение из словаря. map["key"] имеет тип со знаком ?: ключа может не быть. Используйте map["key"] ?: значение, map.getValue("key") (бросит ошибку, если ключа нет) или map.getOrDefault(...).

Функции с OrNull в имени — toIntOrNull(), firstOrNull(), getOrNull() — возвращают null при неудаче. Это сделано специально: обработайте null через ?:.

Проверка есть, а ошибка осталась

Если s — изменяемое свойство класса (var), проверка if (s != null) не помогает: между проверкой и использованием значение могло измениться. Компилятор пишет, что умное приведение невозможно (smart cast is impossible): даже после проверки он не может считать s обычной строкой. Скопируйте значение в локальную val и работайте с ней:

Kotlin
class Profile {
    var nickname: String? = null

    fun show() {
        val n = nickname
        if (n != null) {
            println(n.length)
        }
    }
}

Или короче: nickname?.let { println(it.length) }.

Не исправляйте каждую такую ошибку добавлением !! — так ошибка из сборки переезжает в работу программы, и она упадёт у пользователя. !! уместен, только когда null невозможен, а компилятор этого не видит.