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

NullPointerException в Kotlin

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

В Kotlin обычные типы не бывают null, и большинство ошибок с пустыми значениями компилятор ловит ещё до запуска. Если программа всё же упала с NullPointerException, сначала найдите строку с ошибкой, а потом по ней — свою причину: почти всегда это одна из четырёх.

Как найти строку с ошибкой

Сообщение выглядит так:

Текст
Exception in thread "main" java.lang.NullPointerException
    at MainKt.main(Main.kt:3)

Под первой строкой сообщения идёт стек вызовов — строки at …: через какие функции прошла программа до ошибки. Нужна первая строка, где упомянут ваш файл. Main.kt:3 — файл и строка. MainKt — имя, под которым Kotlin собирает функции из файла Main.kt. В IntelliJ IDEA имя файла в окне Run — ссылка: щелчок переносит прямо на строку.

Причины и что делать

По этой строке и тексту сообщения выберите свой случай.

В строке есть !!замените его безопасным вариантом

Такая программа падает как раз с сообщением выше — на строке 3:

Kotlin
fun main() {
    val name: String? = null
    println(name!!.length)
}

!! значит «здесь точно не null». Если значение всё-таки null, программа падает. Замените !! безопасным вариантом:

Kotlin
fun main() {
    val name: String? = null
    println(name?.length ?: 0)
    if (name != null) {
        println(name.length)
    }
}
Вывод
0

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

Дальше: «Если программа на Android»

Значение пришло из кода на Javaпроверьте его на null

Библиотеки на Java не сообщают Kotlin, может ли их функция вернуть null. Тип такого значения IntelliJ IDEA показывает со знаком ! — например, String! — это платформенный тип: компилятор разрешает работать с ним как угодно, а проверка ложится на программиста. Если функция Java вернула null, а код Kotlin обратился к нему, будет NullPointerException.

Решение — сразу положить результат в переменную с явным типом со знаком ? и проверить:

Kotlin
fun main() {
    val home: String? = System.getenv("NO_SUCH_VARIABLE_123")
    println(home ?: "not set")
}
Вывод
not set

System.getenv из Java возвращает null, если такой переменной окружения нет.

Дальше: «Если программа на Android»

Сообщение про lateinitсвойство прочитано раньше времени

Свойство с lateinit прочитали раньше, чем присвоили. Ошибка тут другая, но похожая:

Текст
kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized

Как с этим работать — в справке lateinit — отложенная инициализация.

Дальше: «Если программа на Android»

Свойство читается из initпорядок создания объекта

Свойства класса получают значения сверху вниз. Если метод, вызванный из init или из конструктора родителя, обращается к свойству, которое ещё не получило значения, оно может оказаться null даже с типом без вопроса. Чаще всего это вызов open-метода из конструктора родительского класса. Не вызывайте из init методы, которые переопределяют наследники.

Если программа на Android

В Android то же сообщение появляется в окне Logcat Android Studio, строка с ошибкой ищется так же — по имени своего файла в строках at. Частые причины там — !! у значений, которые приходят из системы, и lateinit, прочитанный раньше, чем экран успел его заполнить.