Late-Initialized Variables and Lazy Initialization in Kotlin

Late-Initialized Variables and Lazy Initialization in Kotlin

Introduction

Welcome to one step closer to your success, Today, deep dive into two concepts that might seem confusing at first, but don't worry we will go through the concepts of late-initialized variables and lazy initialization in Kotlin. By the end of this blog, you'll clearly understand when and how to use these features effectively.

Late-Initialized Variables

Assume we encountered a scenario in which we cannot assign a value to a variable at the time of declaration. In such scenarios, we can use lateinit modifier to achieve this behavior.

class MyClass {
    lateinit var myLateInitVar: String

    fun initializeVar() {
        myLateInitVar = "Hello, late initialization!"
    }

    fun printVar() {
        if (::myLateInitVar.isInitialized) {
            println(myLateInitVar)
        } else {
            println("The variable has not been initialized yet.")
        }
    }
}

fun main() {
    val myClass = MyClass()
    myClass.printVar()
    myClass.initializeVar()
    myClass.printVar()
}

//OUTPUT
//The variable has not been initialized yet.
//Hello, late initialization!

In Kotlin, isInitialized is a property that is used to check if a late-initialized variable has been initialized or not. It can only be used with late-initialized properties and helps to avoid potential runtime errors that might occur if you access a late-initialized variable before it has been initialized.

It's important to use isInitialized with late-initialized variables to ensure they are properly initialized before accessing their values to avoid any potential UninitializedPropertyAccessException errors.

Late-initialized variables have some rules to follow:

  • They must be of non-null types.

  • They cannot be used with primitive data types.

Lazy Initialization

Lazy initialization allows us to defer the creation of an object until it's actually needed. This can be useful when constructing objects that might be resource-intensive or time-consuming.

class MyLazyClass {
    val myLazyVar: String by lazy {
        println("Lazy initialization in progress...")
        "Hello, lazy initialization!"
    }
}

In the above example, myLazyVar is initialized only when it is accessed for the first time. Subsequent accesses will reuse the already initialized value.

Key DifferenceLate-Initialized VariablesLazy Initialization
Initialization TimeMust be initialized before use, typically during runtime, as the compiler won't enforce initializationDelayed initialization until the variable is accessed for the first time
NullabilityCannot be declared with nullable typesThe type can be nullable, and the lazy initialization block can return a null value
Performance ConsiderationsSlightly more efficient as there is no additional overhead for lazy initializationSuitable for resource-intensive objects that might not be needed in all scenarios

Conclusion

By deferring variable instantiation and assignment until they are truly needed, you can optimize resource usage, reduce memory overhead, and improve the overall performance of your code. However, it is essential to carefully consider the context in which you apply these techniques to ensure they provide the intended benefits.

Happy coding :)