Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advantage of lateinit over null initialization in java?

Tags:

java

kotlin

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"
        }
    }
}
like image 282
ssmid Avatar asked Jun 05 '20 12:06

ssmid


3 Answers

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.

like image 78
Nikolai Shevchenko Avatar answered Sep 22 '22 03:09

Nikolai Shevchenko


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) }
}
like image 28
Animesh Sahu Avatar answered Sep 24 '22 03:09

Animesh Sahu


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

like image 24
Alexey Romanov Avatar answered Sep 20 '22 03:09

Alexey Romanov