Я хотел бы добавить немного глубокого погружения в свойства lateinit в Kotlin.
Модификатор 'lateinit' недопустим для свойств типа nullable - это можно найти в документации Kotlin,Этот вид модификатора для специальных видов конструкций. Это для полей, которые будут инициализированы когда-нибудь после создания объекта . Например, через DI-фреймворк или фреймворк-фреймворк.
Но что находится под этим полем? Если мы проверим это, мы просто обнаружим, что перед инициализацией свойство имеет значение null
. Ни больше, ни меньше, просто null
. Но, если мы хотим получить доступ к этому свойству до инициализации UninitializedPropertyAccessException
.
В Kotlin 1.3 lateinit
свойства получили новое свойство - isInitialized
(чтобы использовать его: ::lateinitiProperty.isInitilized
). Таким образом, прежде чем мы получим доступ к этому свойству, мы можем проверить, находится ли в этом поле null
или что-то еще, без исключения.
Но, lateinit
означает, что объект будет инициализирован позже как не нулевое свойство,И программист гарантирует, что это значение не будет нулевым после инициализации. Если это так, почему бы просто не использовать тип nullable
?
Если есть способ деинициализировать свойство lateinit? Да, это. Посредством отражения мы можем снова установить это значение на ноль (JVM не является нуль-безопасным). И доступ к этому полю закончится не с NPE
исключением, а с UninitializedPropertyAccessException
. И .isInitialized
вернет false для поля, которое ссылается на ноль.
И как это работает?
class MyClass {
lateinit var lateinitObject: Any
fun test() {
println("Is initialized: ${::lateinitObject.isInitialized}") // false
lateinitObject = Unit
println("Is initialized: ${::lateinitObject.isInitialized}") // true
resetField(this, "lateinitObject")
println("Is initialized: ${::lateinitObject.isInitialized}") // false again
lateinitObject // this will throw UninitializedPropertyAccessException
}
}
fun resetField(target: Any, fieldName: String) {
val field = target.javaClass.getDeclaredField(fieldName)
with (field) {
isAccessible = true
set(target, null)
}
}
Ofc, использование латинита таким способом, вероятно, не то, что вы хотите, и относитесь как к курьезу по поводу lateinit
дизайна в JVM.
И из-за твоего учителя - он был не прав. Даже если lateinit
может ссылаться на нуль (и это действительно так), вы не можете объявить его как обнуляемый тип. Если вам нужно, вам не нужен модификатор lateinit
.