【问题标题】:Advantage of lateinit over null initialization in java?与Java中的空初始化相比,lateinit的优势?
【发布时间】:2020-06-05 12:34:34
【问题描述】:

我正在尝试一些 Android 的东西并在学习 Kotlin 的过程中,我想知道如何初始化视图和属性。

据我了解,Kotlin 和 Java 中的合约(“我将在使用前初始化”)以及 UninitializedPropertyAccessException 和 NullPointerException 或多或少是等价的。在这两种情况下,您都可以进行 isInitialized 检查。我只是不知道为什么 JetBrains 会如此费心地考虑 null-safety,然后以不同的形式引入完全相同的东西。

那么,lateinit有什么优势吗?

示例代码:

public class Foo {
    private String bar = null;

    public void bar123() {
        if (bar == null) {
            bar = "bar";
        }
    }
}

class Foo {
    private lateinit var bar: String

    fun bar123() {
        if (!::bar.isInitialized) {
            bar = "bar"
        }
    }
}

【问题讨论】:

  • 优势在于,由于var bar 被声明为String 而不是String?,因此您不需要执行空检查,因此您的代码(即bar)是清洁工
  • 但在我看来,if (!::bar.isInitialized) 与空检查几乎相同。我看不出 Kotlin 版本在哪里更干净。
  • @Anonymous,我的意思是在初始化代码(构造函数/构建器/等)之外进行额外的空检查。你避免做所有那些!! 接收者期望String 而你传递String?
  • @NikolaiShevchenko 好的,在课堂外避免 null 对我来说完全有意义。只是试图返回一个未初始化的属性并得到一个异常。你想提交那个作为答案吗?然后我可以将问题标记为已解决。
  • @Ano.Nymous 一旦初始化,您就不能再将其设置为 null。

标签: java kotlin


【解决方案1】:

这个想法是让编译器知道该属性是不可为空的,尽管稍后会对其进行初始化。这将减少接收方代码中对该属性的空值检查。

class Foo {
    lateinit var prop: String
}

class Bar {
    var prop: String? = null
}

fun consumeNotNull(arg: String) {
    println(arg)
}

fun main() {
    val foo = Foo()
    consumeNotNull(foo.prop) // OK

    val bar = Bar()
    consumeNotNull(bar.prop) // Error: Type mismatch: inferred type is String? but String was expected
    consumeNotNull(bar.prop!!) // OK
}

想象一下bar.prop 在 N 个地方被引用。然后在每个地方你都必须对它“尖叫”(bar.prop!!)让编译器开心。 lateinit 机制可以让你避免这种情况并变得更“安静”:) (并保持你的代码更干净)

当然,如果Foo::prop在运行时使用时没有初始化,你会得到异常:

UninitializedPropertyAccessException:lateinit 属性 prop 尚未初始化

但与 NullPointerException 相比,它更具描述性。

【讨论】:

  • 这是lateinit的唯一用例吗?
【解决方案2】:

lateinit 变量的另一种用途是,一旦它被初始化,您就永远无法使其未初始化,“因此,一项检查将确保它永远不会为 null 或被任何其他线程更改”。

class Foo {
    lateinit var prop: String
}

class Bar {
    var prop: String? = null
}

fun main() {
    val foo = Foo()
    foo.prop = "Hello"
    // You can never make it uninitialized now, you can only change it.
    // A single isInitialized is ok. (Rather than checking everytime, because it can be null again)

    val bar = Bar()
    bar.prop = "String"
    println(bar.prop!!)
    bar.prop = null
    println(bar.prop!!) // KotlinNullPointerException, check everytime you use it with ?. operator
    // Call when not null: bar.prop?.let { println(it) }
}

【讨论】:

    【解决方案3】:

    除了 Nikolai Shevchenko 的回答:即使在课堂内,我也会认为 isInitialized 可能表明可空属性可能更有用。

    lateinit 的主要用例是当您无法在构造函数中初始化属性但 可以 保证它在某种意义上“足够早”初始化,而大多数用途不会需要isInitialized 检查。例如。因为某些框架在构造后立即调用一个方法对其进行初始化。

    其实原本没有isInitialized;它只出现在in Kotlin 1.2,而lateinit 已经在 1.0 中(我相信)。

    【讨论】:

      猜你喜欢
      • 2011-09-02
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 2012-03-05
      • 2010-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多