Как избежать ошибок при использовании lateinit var в Kotlin
Что проверяют
Понимаете ли вы, что lateinit это обещание компилятору «присвою до первого чтения», которое он не проверяет, а нарушение всплывает крашем в рантайме. Следом обычно спрашивают про binding во фрагменте и про разницу между lateinit и by lazy.
Как отвечать
Начните с ограничений: модификатор ставится только на var с ненулевым типом, без собственных геттера и сеттера и не на примитивах вроде Int или Boolean. Чтение до присваивания бросает UninitializedPropertyAccessException с именем свойства. Это понятнее NPE, но всё равно краш.
Дальше способы не упасть:
- инициализация по жизненному циклу: присваивать там, где точка гарантированно наступает раньше чтения. В
ActivityэтоonCreate(), поля от Hilt доступны послеsuper.onCreate(), во фрагменте инъекция происходит вonAttach(); by lazy: дляval, который можно вычислить при первом обращении. По умолчанию он потокобезопасен (LazyThreadSafetyMode.SYNCHRONIZED);Delegates.notNull(): заменаlateinitдля примитивов;- nullable-поле: для
ViewBindingво фрагменте правильнее_binding: FragmentXBinding?, обнуляемое вonDestroyView(), и геттерbinding get() = _binding!!. Фрагмент в бэкстеке живёт дольше своегоView, иlateinit-биндинг держит мёртвую иерархию в памяти; - конструктор: зависимости, известные при создании объекта, передавайте в конструктор, тогда
lateinitне нужен вовсе.
Проверка ::prop.isInitialized существует, но работает только для свойства, объявленного в этом же классе, во внешнем классе или на верхнем уровне того же файла. Если она разбросана по коду, свойство на самом деле опциональное и должно быть nullable.
Где спотыкаются
Заполняют lateinit-поле в обработчике клика или колбэке сети. После пересоздания Activity или смерти процесса поле снова пустое, а экран уже читает его в onResume().
Хранят binding фрагмента в lateinit var и не замечают утечку, пока её не покажет LeakCanary.
Лечат краш проверкой if (::x.isInitialized) в каждом месте. Ошибка порядка инициализации остаётся, просто экран молча перестаёт работать.