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"
}
}
}
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.
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.
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) }
}
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With