0Pricing
Kotlin Academy · Lesson

lateinit and Lazy Initialization

Use lateinit for late-assigned properties and by lazy for deferred computation.

lateinit and Lazy Initialization is a free Kotlin Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Two Tools for Late Setup

Sometimes you can't initialize a property at construction time. Kotlin gives you lateinit (for var, assigned later) and by lazy (for val, computed on first access).

lateinit Basics

lateinit var defers initialization. The variable must be a non-null, non-primitive var. Access before assignment throws UninitializedPropertyAccessException.

class Service {
    lateinit var name: String
    fun init(value: String) { name = value }
}
fun main() {
    val s = Service()
    s.init("MyService")
    println(s.name)
}

Why Use lateinit?

Use lateinit for dependency injection, test fixtures, or framework-managed lifecycles (Android views, Spring beans) where init happens outside the constructor.

class Repository {
    lateinit var db: String   // assigned later by DI framework
    fun query() = "querying $db"
}
fun main() {
    val r = Repository()
    r.db = "postgres://localhost"
    println(r.query())
}

isInitialized Check

You can check whether a lateinit property has been assigned using ::propName.isInitialized.

class Foo {
    lateinit var bar: String
    fun status() = if (::bar.isInitialized) "set to $bar" else "unset"
}
fun main() {
    val f = Foo()
    println(f.status()) // unset
    f.bar = "hi"
    println(f.status())
}

lateinit Restrictions

You cannot use lateinit on primitive types (Int, Boolean, etc), nullable types, or val. The compiler enforces these rules.

// VALID:
lateinit var name: String
// INVALID:
// lateinit var count: Int       // primitives not allowed
// lateinit val constant: String // val not allowed
// lateinit var maybe: String?   // nullable not allowed
fun main() { name = "ok"; println(name) }

by lazy Basics

by lazy defers a val's initialization until first access. The lambda runs once and the result is cached.

val expensive: String by lazy {
    println("Computing...")
    "result"
}
fun main() {
    println(expensive) // Computing... then result
    println(expensive) // result (cached)
}

Why Use by lazy?

Use by lazy when initialization is expensive and might not be needed (cache it for later use), or when initialization depends on other properties already constructed.

class Config {
    val parsed: Map<String, String> by lazy {
        println("parsing config...")
        mapOf("host" to "localhost", "port" to "8080")
    }
}
fun main() {
    val c = Config()
    println("config object created — not parsed yet")
    println(c.parsed["host"]) // now parses
    println(c.parsed["port"]) // cached
}

lazy Thread Safety Modes

By default, by lazy uses LazyThreadSafetyMode.SYNCHRONIZED. Use PUBLICATION for concurrent-OK init, NONE for single-threaded performance.

val safe: String by lazy { "thread-safe" }
val fast: String by lazy(LazyThreadSafetyMode.NONE) { "fast, single-thread" }
fun main() {
    println(safe)
    println(fast)
}

lateinit vs lazy

lateinit: var, assigned externally. by lazy: val, computed by the property itself on first access.

class Demo {
    lateinit var injected: String         // assigned by caller
    val computed: String by lazy { "computed once" }
}
fun main() {
    val d = Demo()
    d.injected = "from outside"
    println(d.injected)
    println(d.computed)
}

lazy Reading Other Properties

A lazy property can read other properties (initialized earlier) and combine them.

class User(val first: String, val last: String) {
    val fullName: String by lazy { "$first $last" }
}
fun main() {
    val u = User("Ada", "Lovelace")
    println(u.fullName)
}

Practical Pattern

Combine lateinit for DI and by lazy for derived state in the same class.

class App {
    lateinit var config: Map<String, String>
    val host: String by lazy { config["host"] ?: "localhost" }
}
fun main() {
    val app = App()
    app.config = mapOf("host" to "api.example.com")
    println(app.host) // api.example.com
}

Quick Check

Which keyword defers a val property's initialization until first access?

Recap

Use lateinit var for non-null mutable properties assigned later by frameworks or DI. Use val by lazy for read-only values that should be computed once on first access. Check status with ::prop.isInitialized.

Frequently asked questions

Is the “lateinit and Lazy Initialization” lesson free?

Yes — the full text of “lateinit and Lazy Initialization” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “lateinit and Lazy Initialization”?

Use lateinit for late-assigned properties and by lazy for deferred computation. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “lateinit and Lazy Initialization” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Primary Constructor and Property Parameters
  2. init Blocks and Initialization Order
  3. Custom Getters and Setters with field
  4. lateinit and Lazy Initialization
← Back to Kotlin Academy