Advantage of lateinit over null initialization in java?
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).