ПрограммированиеAndroid разработчик

Что такое 'lateinit' и 'lazy' в Kotlin? Объясните отличия, нюансы использования, ограничения и приведите соответствующие примеры кода.

Проходите собеседования с ИИ помощником Hintsage

Ответ

В Kotlin существуют два разных подхода для поздней инициализации свойств: lateinit и lazy.

lateinit — модификатор, применяемый к переменным (var), которые будут инициализированы позже, обычно через DI или в инициализационном блоке. Подходит только для нестатических переменных НЕ nullable- типов. Обращение до инициализации — выбрасывает исключение UninitializedPropertyAccessException.

class MyClass { lateinit var data: String fun init() { data = "Hello!" } }

lazy — специальный делегат, применяемый к val-свойствам (только read-only). Выражение-инициализатор вычисляется при первом обращении к переменной. Безопасно для многопоточного доступа по умолчанию.

val config: Config by lazy { loadConfigFromFile() }
  • lateinit не работает с типами примитивов и val.
  • lazy не допускает переинициализации.
  • lateinit — для переменных, которые гарантированно будут назначены позднее (например, через DI-фреймворк или во фрагменте жизненного цикла Android).
  • lazy — для ленивых вычислений, когда значение дорого по вычислениям и может быть не востребовано.

Вопрос с подвохом

Можно ли использовать lateinit для свойств типа Int?

Ответ: Нет. lateinit работает только с объектными НЕ nullable-типами. Для примитивов и nullable-типов — нельзя. Попытка объявить lateinit var a: Int приведет к ошибке компиляции.

Примеры реальных ошибок из-за незнания тонкостей темы


История

В образовательном проекте объявили переменную конфигурации как lateinit var config: Config?, но компилятор не разрешил это сделать — сэкономили время, но пришлось разобраться, почему работает не так, как ожидали.


История

В Android-приложении забыли проверить на инициализацию lateinit-свойства перед вызовом. Это привело к крашу приложения на проде — дизайнер пытался получить доступ к свойству до onCreateView. Помогли дополнительные проверки.


История

При использовании многопоточности разработчик применил by lazy для инициализации ресурсоемкого объекта без передачи параметра, посчитав, что делегат по умолчанию не потокобезопасен. В итоге в релизе появились проблемы с race condition. Решили через передачу соответствующего параметра для lazy: by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { ... }.