在Java中,使用lateinit相比于空值初始化的优势是什么?

huangapple go评论87阅读模式
英文:

Advantage of lateinit over null initialization in java?

问题

以下是翻译好的内容:

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

据我了解,Kotlin和Java中的契约(“我会在使用之前初始化”),以及UninitializedPropertyAccessException和NullPointerException在很大程度上是等效的。在这两种情况下,您都可以进行isInitialized检查。我只是不知道为什么JetBrains会在空安全方面花费这么多精力,然后又以不同的形式引入完全相同的东西。

那么,使用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";
        }
    }
}
英文:

I was trying some Android stuff and learning Kotlin on the way and I was wondering how to initialize Views and properties in general.

As far as I understand, the contracts in Kotlin and Java ("I will initialize before use") and both UninitializedPropertyAccessException and NullPointerException are more or less equivalent. You can do a isInitialized check in both cases. I just don't know why JetBrains would bother with null-safety so much and then introduce the exact same thing in a different shape.

So, is there any advantage in lateinit?

Example Code:

public class Foo {
    private String bar = null;

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

vs

class Foo {
    private lateinit var bar: String

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

</details>


# 答案1
**得分**: 5

代码部分不需要翻译以下是已翻译的内容

"要点是使编译器知道该属性是非空的,尽管它将在稍后进行初始化。这将减少接收器代码中对该属性的空检查。

假设 `bar.prop` 在 N 处被引用那么在每个地方你都必须对它进行呼喊(`bar.prop!!`)以让编译器满意`lateinit` 机制允许你避免这样做更加安静 :)并保持代码更加清晰

当然如果在运行时使用 `Foo::prop` 时尚未初始化您将收到异常

> UninitializedPropertyAccessException: lateinit property prop has not been initialized

但与 NullPointerException 相比它更加具有描述性"

<details>
<summary>英文:</summary>

The idea is to make compiler aware that the property is non-nullable though it will be initialized later. That would reduce null-checks on this property in the receiver code.

```kotlin
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
}

Imagine that bar.prop is referred to in N places. Then in each place you have to "scream" at it (bar.prop!!) to make compiler happy. lateinit mechanism lets you to to avoid that and be more "quiet" 在Java中,使用lateinit相比于空值初始化的优势是什么? (and keep your code cleaner)

Of course, if Foo::prop isn't initialized by the moment of using it in runtime, you will get exception:

> UninitializedPropertyAccessException: lateinit property prop has not been initialized

but in compare to NullPointerException it's bit more descriptive.

答案2

得分: 3

除了Nikolai Shevchenko的回答之外:即使在类内部,我也认为isInitialized是一个可以更有用地指示可为空属性的指标。

lateinit的主要用例是当您无法在构造函数中初始化属性,但可以确保在某种意义上它在“足够早”的时候被初始化,以至于大多数用途不需要isInitialized检查。例如,因为某些框架在构造后立即调用初始化方法。

实际上,最初是没有isInitialized的;它仅在Kotlin 1.2中出现,而lateinit已经存在于1.0中(我相信)。

英文:

In addition to Nikolai Shevchenko's answer: even inside the class I'd consider isInitialized a likely indicator that a nullable property can be more useful.

The primary use-case for lateinit is when you can't initialize a property in the constructor but can guarantee that it's initialized "early enough" in some sense that most uses won't need an isInitialized check. E.g. because some framework calls a method initializing it immediately after construction.

In fact, originally there was no isInitialized; it only appeared in Kotlin 1.2, and lateinit was already in 1.0 (I believe).

答案3

得分: 2

另一个使用延迟初始化变量的用途是,一旦初始化,就不能使其未初始化,"因此一次检查将确保它永远不会变为 null 或被其他线程更改"。

class Foo {
    lateinit var prop: String
}

class Bar {
    var prop: String? = null
}

fun main() {
    val foo = Foo()
    foo.prop = "Hello";
    // 您现在不能使其未初始化,只能更改它。
    // 单次 isInitialized 检查就足够了。(而不是每次都检查,因为它可能再次变为 null)

    val bar = Bar()
    bar.prop = "String";
    println(bar.prop!!)
    bar.prop = null
    println(bar.prop!!) // KotlinNullPointerException,在使用 ?. 运算符时每次使用时都要检查
    // 不为 null 时调用:bar.prop?.let { println(it) }
}
英文:

One another use of a lateinit variable is that once it is initialized you can never make it uninitialized, "So one check will make it sure that it is never gotta be a null or changed by any other Thread".

class Foo {
    lateinit var prop: String
}

class Bar {
    var prop: String? = null
}

fun main() {
    val foo = Foo()
    foo.prop = &quot;Hello&quot;
    // 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 = &quot;String&quot;
    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) }
}

huangapple
  • 本文由 发表于 2020年6月5日 20:34:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/62215493.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定