在java中,lateinit相对于null初始化的优势是什么?



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

据我了解,Kotlin 和 Java 中的合约("我将在使用前初始化"(以及 UninitializedPropertyAccessException 和 NullPointerException 或多或少是等价的。在这两种情况下,您都可以执行 isInitialized 检查。我只是不知道为什么 JetBrains 会如此烦恼于零安全,然后以不同的形状引入完全相同的东西。

那么,后期有什么好处吗?

示例代码:

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"
}
}
}

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

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相比,它的描述性更强。

除了尼古拉·舍甫琴科的回答:即使在类中,我也认为isInitialized一个可能的指标,表明可为空的属性可能更有用。

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

事实上,最初没有isInitialized;它只出现在 Kotlin 1.2 中,而lateinit已经在 1.0 中(我相信(。

lateinit 变量的另一个用途是,一旦它被初始化,你就永远不能让它未初始化,"所以一次检查将确保它永远不会是空值或被任何其他线程更改"。

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) }
}

最新更新