Является ли ключевое слово lateinit ненужным?

Я изучаю Котлин, и чтение о lateinit слове lateinit заставляет меня сомневаться в его полезности. Рассмотрим этот код:

 var testString: String? = null lateinit var lateTestString: String fun print() { print(testString?.length) print(lateTestString.length) } 

Здесь единственное различие в получении длины строки – это проверить, является ли она нулевой или нет, используя ?. оператор. Использует lateinit ярлык для того, чтобы не добавлять этот дополнительный знак вопроса при доступе к свойствам или методам вызова? Именно по этому факту я считаю, что больше стоит добавить этот дополнительный знак вопроса, чем получить исключение при доступе к lateinit .

Больше исследований показало, что lateinit хорош для инъекций и / или модульных тестов, где переменная еще не была инициализирована, но это будет. Однако не стоит ли этого лишнего ?. а не просто . не подвергать риску исключение?

lateinit слово lateinit существует, чтобы включить один конкретный сценарий: когда ваше поле не может быть нулевым, но вы также не можете его инициализировать в конструкторе или с постоянным значением. Перед вами следует убедиться, что вы инициализируете значение перед его использованием. Если вы этого не сделаете, вы получите особое исключение с четким смыслом.

Разница между использованием lateinit и «нормальным» полем с нулевым значением с ?. заключается в том, что последний передает неверное сообщение о коде: «эта вещь иногда может быть нулевой». Когда на самом деле это невозможно. Он просто инициализируется позже обычного (например, вместо инъекции вместо конструктора).

Использует lateinit ярлык для того, чтобы не добавлять этот дополнительный знак вопроса

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

Эти два !! были выбраны намеренно, чтобы привлечь внимание к местам в коде, где вы, например, «делаете ставку на систему типов». При использовании в надлежащих местах это именно то, что вы хотите, но как насчет всех этих варов в реальных проектах, которые фактически не равны нулю , просто система типов слишком слаба, чтобы доказать это? Мне бы очень хотелось увидеть распространение !! все вокруг моей кодовой базы, когда я могу легко убедиться, что они инициализированы. Этот шум ослабит сильный сигнал, который !! посылает.

Когда вы видите lateinit var в коде, вы знаете, что вам просто нужно искать любой метод инициализации, который указывает окружающий контекст, чтобы убедиться, что все в порядке. Очень легко проверить, что он используется правильно, и я никогда не видел ошибки, вытекающей из него.

На самом деле очень приятно видеть, что дизайнеры Kotlin ставят проблемы реального разработчика над строгими формальными формализмами.

Я не из команды Jetbrains, поэтому, возможно, у меня нет четкой картины, но я согласен с вами в том, что lateinit выглядит не очень хорошо.

Первоначальная идея, когда была добавлена lateinit заключалась в том, что у нас есть некоторые рамки (подсказка: Android), где время от времени у пользователя рамки нет доступа к конструкторам класса (подсказка: класс активности Android), так как он не может инициализировать какое-либо свойство в конструктор или блок init . Но из-за того, что эти классы имеют какой-то жизненный цикл, мы можем быть уверены, что свойство foo будет инициализировано до того, как оно будет использоваться впервые, потому что, например, инициализация происходит в onCreate() , а свойство используется в onResume() , что происходит позже.

(Где-то в прошлом, L – ленивый программист, J – Jetbrains):

L: Эй, Jetbrains! Мы ленивы, нам не нужен этот дополнительный вопросительный знак, если мы уверены, что свойство будет инициализировано. Не могли бы мы пометить его как-то, чтобы преодолеть нулевую безопасность Котлина?

J: Да, конечно! Давайте похлопать в модификаторе lateinit .

Замечательная идея?

Нет.

Потому что в более поздних версиях языка его создатели решили добавить новый синтаксис для свойств lateinit . Я мог ошибаться (не обращал на это особого внимания), но он выглядит как foo::isInitialized . Причина в том, что этот модификатор неправильно используется (или, может быть, он испорчен с самого начала?), Поэтому нам нужно приложить к нему дополнительные проверки.

Итак, в основном, мы foo::isInitialized вопросительным знаком вместе со всей нулевой безопасностью для замечательной возможности выполнить foo::isInitialized проверки, чтобы предотвратить UninitializedPropertyAccessException (или что-то еще).

lateint означает позже инициализировать
в этом коде появляется ошибка. Перед вызовом вы должны инициализировать lateTestString.

 var testString: String? = null lateinit var lateTestString: String fun print() { print(testString?.length) print(lateTestString.length) }