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