lateinit vs lazy in Kotlin
Kotlin
lateinit vs lazy in Kotlin
Akshay Nandwana
January 31, 2025
5 min read
40 views

Kotlin offers two ways to initialize properties lazily: lateinit and lazy. While both are used to delay initialization, they serve different use cases.

In this article, we’ll break down their differences, use cases, and real-world analogies to help you choose the right approach.


Join our upcoming classes
Our Courses


1. Understanding lateinit

What is lateinit?

The lateinit keyword is used for mutable (var) properties that will be initialized later but before first use. It is primarily used with non-nullable types (except primitive types like Int, Double, etc.).

Syntax

kotlin
class User {
    lateinit var name: String
    
    fun setUserName(userName: String) {
        name = userName
    }
    
    fun printName() {
        if (::name.isInitialized) {
            println("User name: $name")
        } else {
            println("Name is not initialized yet.")
        }
    }
}

fun main() {
    val user = User()
    user.setUserName("Akshay")
    user.printName()  // Output: User name: Akshay
}

Real-World Analogy for lateinit

Think of lateinit as a hotel room booking system. When a guest books a room, the reservation is made, but the actual check-in happens later. The room is guaranteed to exist, but the guest hasn't occupied it yet.

When to Use lateinit?

  • When you must initialize a variable before first use, but not in the constructor.

  • When dependency injection frameworks like Hilt or Dagger are used.

  • When you need late property initialization in unit tests.

Advanced Insights on lateinit

✅ lateinit properties can be checked if they are initialized using ::property.isInitialized.

✅ lateinit cannot be used for val, as it is inherently mutable (var).

✅ Hidden Pitfall: If lateinit property is never initialized and accessed, it results in a UninitializedPropertyAccessException.

✅ Alternative: If lateinit is overused, consider using nullable types (String?) with !! assertions instead.

2. Understanding lazy

What is lazy?

The lazy keyword is used for read-only (val) properties and evaluates the assigned value only once, when accessed for the first time.

Syntax

kotlin
class DatabaseConnection {
    val connection: String by lazy {
        println("Establishing Database Connection...")
        "Connected to Database"
    }
}

fun main() {
    val db = DatabaseConnection()
    println("Before accessing connection")
    println(db.connection)  // Output: Establishing Database Connection... Connected to Database
    println(db.connection)  // Output: Connected to Database (without re-evaluating)
}

Real-World Analogy for lazy

Think of lazy as turning on a water heater. The heater remains off until someone actually needs hot water. The moment someone turns on the hot water tap, the heater starts, but once heated, it remains available for subsequent use without restarting.

When to Use lazy?

  • When a variable is expensive to compute (like database queries, API calls, or large object creations).

  • When you don't need the value immediately, but only when first accessed.

  • When ensuring thread safety in multi-threaded environments (lazy by default is thread-safe).

Advanced Insights on lazy

✅ The default lazy mode is thread-safe, meaning it synchronizes initialization across multiple threads.

✅ There are three lazy modes:

  • LazyThreadSafetyMode.SYNCHRONIZED (default, ensures thread-safety)

  • LazyThreadSafetyMode.PUBLICATION (allows multiple initializations but ensures only one is retained)

  • LazyThreadSafetyMode.NONE (no synchronization, best for single-threaded scenarios)

✅ Hidden Pitfall: lazy should not be used for variables that are frequently accessed and require fast retrieval. Since it adds an internal locking mechanism, excessive usage can cause performance overhead.

3. Key Differences: lateinit vs lazy

lateinit

Usage - Works with var (mutable)

Initialization - Must be initialized before first use

Primitive Support - Not allowed

Multi-threading - Not thread-safe

Common Use Cases - Dependency injection, Android views, unit testing

lazy

Usage - Works with val (immutable)

Initialization - Initialized only on first access

Primitive Support - Allowed

Multi-threading - Thread-safe by default

Common Use Cases - Expensive object creation, Singleton patterns, thread safety

4. Which One Should You Use?

Choose lateinit if:

✅ You have a mutable property (var).

✅ You will guarantee initialization before first use.

✅ You’re using dependency injection or Android views.

Choose lazy if:

✅ You have an immutable property (val).

✅ You want to delay computation until first access.

✅ You’re dealing with expensive operations like database connections.

5. Conclusion

Both lateinit and lazy serve different purposes in Kotlin. While lateinit is useful for delaying initialization when you know it will be initialized before use, lazy ensures that a property is initialized only when required, making it ideal for expensive computations.

Understanding their differences will help you write efficient, optimized, and cleaner Kotlin code.

🔥 Senior Engineer's Take

  • Overuse of lateinit can lead to runtime crashes; always check ::property.isInitialized.

  • lazy properties should be used carefully in performance-critical applications.

  • If lateinit and lazy both seem like options, prefer lazy as it avoids mutable state.


Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
Our Courses

Share This Article
Stay Updated

Get the latest Android development articles delivered to your inbox.