Либо автор этого кода просто не думал, либо они также новичок в Kotlin.
Пока объект, который вам нужно создать, не требует наличия рабочего объекта контекста в его конструкторе, он может быть создан вне каких-либо методов. Я знаю, что когда я только начинал разработку для Android и заметил, что некоторые вещи могут быть созданы только в onCreate()
, я пошел по безопасному пути и просто создал все там. Возможно, именно это делал автор кода и передал эту привычку Котлину.
Теперь, как правило, лучший способ обработать переменную поздней инициализации с помощью модификатора lateinit
:
private lateinit var personList: ArrayList<Person>
, а затем создать его экземпляр в onCreate()
. Он больше не может обнуляться, поэтому для него не нужен модификатор !!
.
Конечно, если вы хотите сохранить текущий обнуляемый статус, вы всегда можете просто установить параметр в конструкторе Адаптера на обнуляемый. Вам просто нужно сделать что-то интересное с самим адаптером:
getItemCount(): Int = list?.size!! //or list!!.size
val item = list?.get(position)
item?.whatever
Я могу только строить догадки, но я думаю, что автор просто немного увлекся поздней инициализацией. Оба других параметра не могут быть инициализированы немедленно, поэтому это может быть просто рефлекс, чтобы сделать то же самое для списка.
Честно говоря, !!
здесь на самом деле не принимает эту "ошибку в миллиард долларов" назад. Список инициализируется прямо над personList!!
, поэтому, если у вас не запущен еще один Поток, который может сделать personList
нулевым, прежде чем ваш основной Поток достигнет инициализации Адаптера, personList
гарантированно будет ненулевым.
Что касается "лучшего" способа справиться с нулевой безопасностью ... ну, его нет. Я бы сказал, что «лучшим» способом в этом случае было бы что-то вроде этого:
private val personList = ArrayList<Person>() //this needs to be at the top now
private val adapter by lazy { PersonListAdapter(personList, this) } //by lazy will initialize this variable once it's first referenced and then retain that instance for future calls
private val layoutManager by lazy { LinearLayoutManager(this) }
Теперь все заботится о том, когда класс инициализируется (или когда на переменную ссылаются впервые), и вам не нужно беспокоиться об обнуляемости.
У меня есть пример того, где структура вашего цитируемого кода может вступить в игру: singletons.
class SomeSingleton private constructor() {
companion object {
private var instance: SomeSingleton? = null
fun getInstance(): SomeSingleton {
if (instance == null) instance = SomeSingleton()
return instance!! //since technically instance could've been made null from another Thread, Kotlin requires that you assert the non-null state
}
}
}
Так на самом деле работает by lazy
, если вы посмотрите на декомпилированный проект Kotlin JVM. Конечно, он не будет внутри своего собственного класса, но Kotlin создаст глобальную переменную и метод getVariable()
, который будет делать то же, что и метод getInstance()
, который я описал выше.
Возможно, все это можно заменить на:
class SomeSingleton private constructor() {
companion object {
val instance by lazy { SomeSingleton() }
}
}
TL; DR, я могу только строить догадки, но кажется, что автор придерживается некоторых стилей программирования Java, либо забывая, не осознавая, либо отказываясь использовать альтернативы Kotlin.