skip to content

lateinit

lateinit lets you declare a non-null var that is assigned later, which is how dependency-injected and framework-initialized fields avoid being nullable. The trade you accept is an UninitializedPropertyAccessException on early reads, checkable via ::prop.isInitialized.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

6

What is `lateinit var` in Kotlin and what problem does it solve?

level: juniorimportance: must knowfreq 80%

answer

  1. Deferred init of non-null var
  2. var only, non-null only, no primitives
  3. Avoids making type nullable
  4. UninitializedPropertyAccessException on early read
  5. DI / Android onCreate / test @BeforeEach

basics

~20 s

lateinit var lets you declare a non-null variable without giving it a value right away. You promise to set it before you read it. It's used when a value isn't known at object creation but will be ready before first use.

solid answer

~40 s

`lateinit` is a modifier on a mutable `var` of a non-null reference type that tells the compiler you will assign it later, before any read. Without it, a non-null property must be initialized in the constructor or at declaration. It's typically used for dependency injection, framework callbacks (e.g. Android `onCreate`, JUnit `@BeforeEach`), or builder-style setup where the value isn't available at construction time. The compiler skips the null check it would normally enforce, so you avoid making the type nullable (`Type?`) just to defer assignment. If you read it before assigning, you get an `UninitializedPropertyAccessException`. You can guard with `::prop.isInitialized` to check assignment state.

code

kotlin · 11 lines
kotlin
class Greeter {
    lateinit var name: String

    fun setup(n: String) { name = n }
    fun greet() = "Hello, $name"   // throws if setup() not called first
}

val g = Greeter()
// g.greet()  // UninitializedPropertyAccessException
g.setup("Ada")
println(g.greet())  // Hello, Ada

go deeper

for a junior

Knows it defers initialization of a non-null var and that you must assign before reading.

for a middle

Explains why it beats a nullable type and lists the var/non-null/no-primitive constraints.

for a senior

Frames it as moving the non-null guarantee from compile time to runtime and names real use cases (DI, lifecycle, tests).

for a principal

Discusses design trade-offs of giving up compiler verification and when an alternative (lazy, constructor injection, nullable) is the better contract.

## What `lateinit` means Kotlin enforces null safety: a property with a non-null type (like `String`, not `String?`) must hold a value at all times. Normally that means you initialize it in the constructor or at the declaration site. But sometimes the real value isn't available yet at construction time — for example a field injected by Spring/Dagger, or a view assigned in Android's `onCreate`, or a test fixture set up in `@BeforeEach`. `lateinit` is a modifier that says: *"This non-null `var` will be assigned later, before anyone reads it — trust me, compiler."* ```kotlin class UserService { lateinit var repository: UserRepository // no value yet fun init(repo: UserRepository) { repository = repo // assigned later } fun findUser(id: Long) = repository.find(id) // safe if init() ran first } ``` ## The alternative it replaces Without `lateinit`, you'd either: - Make it nullable: `var repository: UserRepository? = null` — then every use needs `?.` or `!!`, polluting the code with null handling for something you know is non-null after setup. - Initialize with a dummy/default value — often impossible or wasteful. `lateinit` keeps the type non-null and avoids both. ## Key constraints - Only on `var` (mutable), never `val`. - Only on **non-null** types (`String`, not `String?`). - **Not** allowed on primitive types like `Int`, `Boolean`, `Double` (use `by Delegates.notNull()` instead). - No custom getter/setter. - Can be a member property, top-level property, or local variable. ## What happens on early access Reading before assignment throws `UninitializedPropertyAccessException` ("lateinit property X has not been initialized"). You can defensively check with the reference syntax `::repository.isInitialized`. ## Mental model `lateinit` is a **deferred, unchecked promise** of non-null. The benefit is clean non-null types; the cost is that the compiler can no longer guarantee initialization — that responsibility moves to you, enforced at runtime instead of compile time.

  • Why not just use a nullable type instead?
    A nullable type forces null handling (`?.`, `!!`) on every access even though the value is logically always present after setup. `lateinit` keeps the type non-null so call sites stay clean.
  • Can `lateinit` be applied to a `val`?
    No. `lateinit` requires a `var` because assignment happens after declaration; a `val` can only be assigned once and must be initialized at its single assignment point.

Like reserving a parking spot with your name on it before your car arrives — the slot exists and is non-null, but trying to drive away before the car shows up fails.

saying these in an interview costs you the question

  • Claiming `lateinit` works on `val`
  • Saying it works on primitives like `Int`
  • Thinking it gives the property a default value (it doesn't)
  • Confusing it with `lazy` (which auto-initializes on first read)
  • Believing early access returns null instead of throwing

context

open as a page

What are the exact restrictions on where `lateinit` can be used, and how do you defer initialization of a primitive like `Int`?

level: middleimportance: must knowfreq 60%

basics

~20 s

lateinit only works on a mutable var with a non-null, non-primitive type and no custom getter/setter. For a primitive like Int, you can't use lateinit; instead use by Delegates.notNull<Int>(), which throws if read before being set.

open as a page

What exactly happens when you read a `lateinit var` before assigning it, and how does this differ from a nullable property?

level: middleimportance: should knowfreq 55%

basics

~20 s

Reading a lateinit var before it's set throws an UninitializedPropertyAccessException with a message naming the property. A nullable property instead returns null, which you can handle with ?. or ?: — it never throws on read.

open as a page

How does `::prop.isInitialized` work, and from where can you call it?

level: middleimportance: should knowfreq 50%

basics

~10 s

::prop.isInitialized checks whether a lateinit property has been assigned yet, returning true or false without throwing. You usually call it from inside the same class using this::prop.isInitialized to avoid an exception before first use.

open as a page

What are the design risks of `lateinit`, and how would you decide whether to use it in a class API?

level: seniorimportance: should knowfreq 40%

basics

~20 s

lateinit lets objects exist in a half-built state, so calling methods in the wrong order crashes at runtime instead of compile time. Use it only when an external system controls timing (DI, lifecycle); otherwise prefer constructor injection or lazy so the value is guaranteed present.

open as a page

Compare `lateinit var` with `by lazy`. When would you choose each?

level: seniorimportance: should knowfreq 65%

basics

~20 s

lateinit var is a mutable value you assign yourself from outside, later. by lazy is an immutable val that computes itself automatically the first time it's read. Use lateinit when something else supplies the value; use lazy when the object can compute it on demand.

open as a page