OkKotlin

Safely accessing lateinit variables

Kotlin, by design, doesn't allow a non-null variable to be left uninitialised during its declaration.

To get past this issue, Kotlin's lateinit allows us to declare a variable first and then initialise it some point in the future during our program's execution cycle.

The concept is simple, but when we try to access an uninitialised property, it's a different story. Let's talk more about that.

First, let's crash our app

Whenever we declare a lateinit var, we need to initialise it before we can access it. Failing to do that will result in an exception as follows:

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property fullName has not been initialized at UninitializedPropertyKt.main(UninitializedProperty.kt:3)

The above is not one of those warnings that we can safely ignore and move on. An exception like this will crash our app.

And this is a common situation. For example, in an Android app:

We might have a list of news items that we need to fetch from a remote server. When the data finally arrives, we need to initialise a RecyclerView adapter, let's say NewsAdapter based on that data to show up the news articles.

What do you think will happen when we try to access that adapter before we initialise it with the fetched data?

Our app crashes because of the exception we talked about earlier. The lateinit var adapter: NewsAdapter has not been initialised.

Does that mean lateinit properties are useless? No.

Here's a secret:

Before accessing the property, we can check if it's initialised or not.

There's a reflection based API

On Kotlin 1.2 and up, we can quickly check whether a lateinit property has been initialised or not using a reflection based API.

Here's how it works:

lateinit var fullName: String
    
if (::fullName.isInitialized) {
    print("Hi, $fullName")
}

You can read more about this API in the official blog post announcing this update.

The backing field might not be accessible always

There's a small caveat here. This reflection API works only on properties which are defined:

  • In the same class where the check is being made
  • In an outer class
  • As top-level in the same file

So, if we have a lateinit property in one class and try to check whether it's initialised or not in another class, like this:

class LateComer {
    lateinit var student: Student
}
    
class School {
    val lateComer = LateComer()
    
    fun printLateComer() {
        if (lateComer::student.isInitialized) {
            println("Late Comer: ${lateComer.student}")
        }
    }
}

The program will get a compile time error saying:

Error:(9, 32) Kotlin: Backing field of 'var student: Student /* = String */' is not accessible at this point

In some case, where we need to verify whether lateinit property of some class is initialised or not, there's a workaround.

Taking note from this StackOverflow answer, we can check lateinit properties of other classes by adding a new method on the target class:

class LateComer {
    lateinit var student: Student
    
    fun isStudentInitialised() = ::student.isInitialized
}

It is not an elegant solution but it works.

Safe to use

This API is neither experimental nor unstable. I have been using it for a long time, and it works every time.

Feel free to take advantage of this small feature and instead of ditching lateinit altogether and falling back to nullable properties.

Here's a sketch note on the topic

Lateinits sketch note