lateinit vs lazy in Kotlin

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

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

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

Love from our past students

Excellent list of questions really helped me to coverup all the topics before interview.

Saiteja Janjirala

10th Oct, 2024

I had an exceptional experience with the 1:1 mentorship session. Akshay was incredibly friendly and provided invaluable guidance on focusing on long-term goals. They also gave great interview tips, including a thorough resume review. Additionally, the discussion on Data Structures and Algorithms (DSA) was insightful and practical. Highly recommended for anyone looking to advance their career!

Nayab khan

11th Sep, 2024

Cleared my major points for what I am missing in the resume and also suggested what I can work on for further growth in the career.

Ketan Chaurasiya

7th Aug, 2024

What impressed me most was his personalized approach and practical tips that I could immediately apply. Akshay’s guidance not only improved my technical skills but also boosted my confidence in navigating my career path. His insights and encouragement have been a game-changer for me. I highly recommend Akshay’s mentorship to anyone looking to advance their Android development career.

Hardik Kubavat

5th Aug, 2024

2025© Made with   by Android Engineers.