Skip to Content
Course content

152: Late-Initialized Properties (lateinit)

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember working with a developer a few years back who was migrating a large Java project to Kotlin. He had this DatabaseClient that needed to be initialized during a bootstrap phase—long after the class was instantiated, but before any business logic actually touched it. He did the "safe" thing and declared it as a nullable var DatabaseClient? = null. The problem is that he knew, with absolute certainty, that by the time any method was called, that client would be initialized. He spent half his day typing !! after every single reference to the client. It made the code noisy, fragile, and honestly, it defeated the whole point of Kotlin's null safety.

Breaking the Nullability Cycle

This is exactly where lateinit comes in. When you use lateinit var, you're essentially making a promise to the Kotlin compiler. You're saying, "I know you want this initialized in the constructor, but I can't do that right now. I promise to set its value before I ever try to read from it."

The magic here is that the property is treated as non-nullable. You don't have to deal with optional chaining (?.) or the dangerous non-null assertion operator (!!). You just use it like a regular, non-null variable. This is incredibly common in Android development (think onCreate) or when using dependency injection frameworks where the framework "injects" the dependency after the object is created.

class UserProfileManager {
    // I can't initialize this here because I need the API key from a config file first
    lateinit var sessionToken: String

    fun initializeSession(token: String) {
        sessionToken = token
    }

    fun printSession() {
        // No null-checks needed!
        println("Current session is: $sessionToken")
    }
}

Now, there are a few strict rules you need to follow. First, lateinit only works with var, not val, because the value has to be changeable after the object is constructed. Second, it only works with non-nullable types. If you've already declared it as String?, lateinit is redundant and won't be allowed.

Verifying Initialization Status

Since you're promising the compiler that the variable will be ready, the compiler stops checking for you. If you break that promise—meaning you try to access the property before assigning a value to it—Kotlin will throw an UninitializedPropertyAccessException. It's a loud, crashing failure, which is actually preferable to a silent NullPointerException because it tells you exactly what went wrong.

Sometimes, however, you're in a situation where you aren't 100% sure if the initialization has happened yet. You don't want to crash the app just to check. In those cases, you can use a reflection-based check using the :: operator. I don't use this often, but it's a lifesaver in complex lifecycle events.

if (this::sessionToken.isInitialized) {
    println("We are good to go!")
} else {
    println("Still waiting for the token...")
}

Just a quick tip: don't over-use this. If you find yourself using lateinit everywhere, it might be a sign that your class is taking on too many responsibilities or that your dependency graph is a bit too tangled. Use it for the specific cases where the lifecycle of the object and the availability of the data simply don't align.




📋 Practical Task

Refactoring the AppConfigurationStore

You've inherited a piece of code for an AppConfigurationStore. The previous developer used nullable types and the !! operator to handle a configuration string that is loaded from a remote server. This is making the code hard to read and prone to runtime crashes.

Your Task:

  • Refactor the appConfigUrl property to use lateinit.
  • Remove the nullable type declaration (String?).
  • Remove the non-null assertions (!!) from the fetchSettings() method.
  • Ensure the loadUrl() method correctly assigns the value to the lateinit property.
class AppConfigurationStore {
    // Change this to lateinit
    var appConfigUrl: String? = null

    fun loadUrl(url: String) {
        appConfigUrl = url
    }

    fun fetchSettings() {
        // Remove the !! and treat it as a non-null String
        println("Fetching settings from ${appConfigUrl!!}")
    }
}

fun main() {
    val store = AppConfigurationStore()
    store.loadUrl("https://api.example.com/config")
    store.fetchSettings()
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.