英文:
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"
(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 = "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) }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论