Lateinit Variables in Kotlin

Learn via video courses
Topics Covered

Overview

As developers, we often encounter situations where we need to declare variables without an initial value, knowing that they will be assigned a value later in the program's execution. Traditionally, this has been achieved using nullable types or by using the lateinit in Kotlin.

Let's dive in!

lateinit in Kotlin

lateinit in Kotlin is used to tackle the challenge of initializing non-null variables at a deferred time. lateinit in Kotlin has proven to be extremely valuable, especially when working with frameworks like Android, where initialization of certain properties is often deferred until after the object creation.

Key Features

lateinit in Kotlin is a powerful feature with several key characteristics that make it useful in various scenarios:

  • Deferred Initialization:
    With lateinit in Kotlin, you can declare non-nullable properties without providing an initial value at the time of declaration assuring that it will be initialized before it's accessed for the first time.
  • Mutable Properties (vars) Only:
    lateinit in Kotlin can only be used with mutable properties (variables declared with the var keyword) and not with read-only properties (variables declared with the val keyword).
  • Non-Nullable Reference Types Only:
    lateinit in Kotlin can only be used with properties of non-nullable reference types.
  • Check for Initialization:
    Before accessing a property defined as lateinit in Kotlin, it's essential to check whether it has been initialized to avoid an UninitializedPropertyAccessException. This can be done using the isInitialized property reference check.
  • Useful in Dependency Injection:
    lateinit in Kotlin is commonly used in dependency injection scenarios, where the actual initialization of properties is managed by dependency injection frameworks at runtime.
  • Compile-Time Checks:
    The compiler enforces the rules and restrictions associated with lateinit in Kotlin. This ensures that you handle lateinit properties correctly, making the code more reliable and less prone to runtime errors.
  • Null Safety:
    Once a lateinit property is initialized, it becomes non-null, and Kotlin's null safety guarantees apply to it.

“lateinit” Keyword

lateinit in Kotlin is used to declare variables that will be initialized at a later point in code.

However, it's important to note that lateinit in Kotlin cannot be used with properties of primitive data types (like Int, Double, Float) or nullable properties.

“lateinit” Variable

A variable declared using the "lateinit" keyword is referred to as a "lateinit" variable.

Syntax:

where DataType should be a non-primitive and non-nullable data type.

Here's an example on how to use it.

Code:

Output:

Explanation:

In this example, we have a class ExampleClass with a lateinit property lateInitializedString. We first create an instance of ExampleClass and call the initializeString() function to assign a value to lateInitializedString, and then call printString() to successfully print "Hello, World!".

How to Check if A “lateinit” Variable Has Been Initialized?

To check if a variable defined as lateinit in Kotlin has been initialized, you can use the ::isInitialized property on the variable. This property returns a Boolean value indicating whether the variable has been assigned a value or not.

Syntax:

If you are calling the method from an inner class, you need to mention the class name as well. So the syntax changes to:

Return Type:

Here is an example of how you can use it:

It is essential to note that attempting to access the value of a variable defined as lateinit in Kotlin before it has been initialized will result in an UninitializedPropertyAccessException. Therefore, always check if it has been initialized before using it to avoid this exception.

Examples

Let us look at a few examples to understand lateinit in Kotlin better.

Example 1: Using lateinit with a String Property

Consider the following code snippet.

Code:

Explanation:

In this example, we declare a class MyClass with a lateinit var myString: String property. The initializeString() function is used to assign a value to myString. In the printString() function, we check if myString has been initialized using the ::isInitialized property before printing its value. Since myString was initialized, it passed the check if (::myString.isInitialized) & we got the output as "Hello, World!"

Example 2: Accessing the lateinit String Property that Is Not Initialized

In the main() function of the above program, let us try calling the printString() function without calling initializeString()

Code:

Explanation:

In the printString() function, we check if myString has been initialized using the ::isInitialized property before printing its value. Since we have not initialized myString, it does not pass the check if (::myString.isInitialized). Hence, we got the output as "The string has not been initialized."

Example 3: Using lateinit with a Custom Class Property

Consider the following code snippet. Code:

Output:

Explanation:

In this example, we declared a class Shape with a lateinit var center: Point property, where Point is a custom class representing coordinates. The initializeCenter() function initializes the center property with a new Point object. The printCenter() function checks if center has been initialized before printing its coordinates. Since the variable center is initialised, it passes the checkif (::center.isInitialized) and prints the output "Center coordinates: (0, 0)"

Lifecycle-driven Properties and “lateinit”

In Android development, data binding is a classic example of utilizing lateinit in Kotlin to postpone the initialization of an activity. It is a common practice among developers to initialize the binding variable early to reference different views within other methods effectively.

In the provided code for the ExampleActivity class below, we utilize the modifier lateinit in Kotlin when declaring the binding variable to achieve the same purpose. Code:

The binding for ExampleActivity can only be initialized once the activity lifecycle function, onCreate(), is called. Hence, using the lateinit modifier in this scenario is entirely justified.

When to Use “lateinit”?

If you don't use lateinit in Kotlin, then you need to initialize the variable with a dummy value, generally null. So every time before you access the variable, you have to make a null check. Consider the following code snippets.

Without lateinit initialization Code:

With lateinit initialization Code:

As we can see, using lateinit initialization reduces the code verbosity by reducing these null checks.

Things to Remember When Using Lateinit

  • Always remember to initialize lateinit properties before accessing them to avoid exceptions during runtime.
  • Use a var declaration to keep the property mutable; using val and const is inappropriate as they indicate immutable properties incompatible with lateinit in Kotlin.
  • Refrain from using lateinit in Kotlin with properties of primitive data types or when the likelihood of a null value is high, as it is not designed for such cases and doesn't support primitive or nullable types.

Conclusion

  1. lateinit in Kotlin is a modifier that allows us to declare properties without initializing them immediately, deferring the initialization to a later point in the code.
  2. It can only be used with mutable properties declared with the var keyword, as it requires the property to be assignable after declaration.
  3. Using lateinit in Kotlin can be helpful when the exact value of a property cannot be determined during object creation, but it will be initialized before being accessed.
  4. Before using a lateinit variable, it is essential to check if it has been initialized using ::isInitialized to avoid an UninitializedPropertyAccessException.
  5. lateinit in Kotlin is not suitable for primitive data types or nullable types, as it only works with non-null reference types.
  6. Use lateinit in Kotlin only when it's necessary, as it bypasses the safety checks of nullable types and may lead to runtime exceptions if not used carefully.