Это правильно "lateinit var text: String?"? - PullRequest
2 голосов
/ 21 октября 2019

Мне нужно знать, правильный ли этот строковый код, мой учитель сказал мне, что это правильно, но я не согласен, потому что «lateinit» не может быть с переменной, которая может быть нулевой или нет. Код строки:

    lateinit var text : String?

Код:

    val cadena = null
    lateinit var text : String?
    text = null
    text = cadena ?: "Hola"
    text?.let { println(text) }

Ответы [ 2 ]

3 голосов
/ 21 октября 2019

Вы правы, а ваш учитель не прав. Доказательство: lateinit var text : String? приводит к ошибке компиляции с Kotlin 1.3.50 :

Недопустимый модификатор 'lateinit' в свойствах типов, допускающих значение Nullable

То, как любой учитель может утверждать, что такой код верен, мне не подходит ...

1 голос
/ 21 октября 2019

Я хотел бы добавить немного глубокого погружения в свойства 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.

...