2

我需要知道这行代码是否正确,我的老师告诉我它是正确的,但我不同意,因为“lateinit”不能与可以为空或不为空的变量一起使用。线路代码:

    lateinit var text : String?

代码:

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

2 回答 2

3

你是对的,你的老师是错的。证明:lateinit var text : String?导致Kotlin 1.3.50编译错误:

可空类型的属性上不允许使用“lateinit”修饰符

任何老师怎么可能声称这样的代码是正确的,这超出了我的范围......

于 2019-10-21T04:54:22.847 回答
1

我想深入探讨 Kotlin 中的 lateinit 属性。

可空类型的属性不允许使用“lateinit”修饰符- 这可以在 Kotlin 文档中找到。这种修饰符适用于特殊类型的结构。它用于在对象创建后将被初始化的字段。例如,通过 DI 框架或模拟框架。

但是,那个领域是什么?如果我们检查它,我们会简单地发现在初始化之前的属性是有null值的。仅此而已,仅此而已null。但是,如果我们想在UninitializedPropertyAccessException引发初始化之前访问该属性。

在 Kotlin 1.3中lateinit,属性获得了新属性 - isInitialized(使用它:) ::lateinitiProperty.isInitilized。因此,在我们访问该属性之前,我们能够检查该字段下是否是null或其他内容,而不会引发异常。

但是,lateinit意味着该对象稍后将被初始化为非空属性。并且程序员保证初始化后这个值不为空。如果可以,为什么不使用nullable类型?

如果有办法取消初始化 lateinit 属性?是的。通过反射,我们可以再次将该值设置为 null(JVM 不是 null 安全的)。并且访问该字段不会以NPEexecption 结束,而是以UninitializedPropertyAccessException. 并且.isInitialized将为引用 null 的字段返回 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 可能不是您想要的,并且将其视为有关lateinitJVM 设计的古玩。

由于你的老师 - 他是不对的。即使lateinit可以引用 null(实际上确实如此),您也不能将其声明为可为 null 的类型。如果需要,则不需要lateinit修饰符。

于 2019-10-21T10:43:41.333 回答