skip to content

lateinit and isInitialized

Deferred non-null initialization as an alternative to making a property nullable, for the cases where a framework or test setup assigns the value after construction. The trade-off between lateinit and a nullable var is a common design question.

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

explore

questions

9

What does `this::myProp.isInitialized` do, and why would you use it on a `lateinit var`?

level: juniorimportance: must knowfreq 55%

answer

  1. Bound property reference: this::prop
  2. Returns Boolean: assigned yet?
  3. Avoids UninitializedPropertyAccessException
  4. lateinit only, declaring class only
  5. Alternative when no 'unset' state: by lazy

basics

~20 s

It checks whether a lateinit property has already been given a value. If you read a lateinit property before it is set, the app crashes; this check lets you ask 'is it set yet?' first and avoid the crash.

solid answer

~40 s

`isInitialized` is a property on a bound property reference (`this::myProp`) that returns a `Boolean` saying whether a `lateinit var` has been assigned. Reading an uninitialized `lateinit` throws `UninitializedPropertyAccessException`; calling `this::myProp.isInitialized` first lets you branch safely without try/catch. It only works on `lateinit` properties, and only from inside the declaring class via a property reference to that property. Typical use: a property set in `onCreate`/`@PostConstruct` or by a framework where you can't guarantee assignment happened before some method runs, so you guard the read. It is not for ordinary nullable types — those use `?.`/`!= null` instead. The check is essentially asking the backing field 'do you hold a real value yet?'.

code

kotlin · 6 lines
kotlin
class Service {
    private lateinit var conn: String
    fun connect() { conn = "open" }
    fun status(): String =
        if (this::conn.isInitialized) conn else "not connected"
}

go deeper

for a junior

Knows lateinit can crash on early read and that isInitialized guards against it.

for a middle

Names the exception, the bound-reference syntax this::prop, and the lateinit-only constraint.

for a senior

Explains it returns Boolean for assignment (not null), the declaring-class scope limit, and when by lazy is the better tool.

for a principal

Frames isInitialized as a code-smell signal: needing it often means the lifecycle/DI model leaks an unset window that design could remove.

## What `lateinit` is `lateinit var` lets you declare a non-null `var` property without an initializer, promising the compiler you'll assign it before first read. It's used when a value is injected or set by a lifecycle method (Android `onCreate`, Spring `@PostConstruct`, test `@BeforeEach`) and a nullable type would force noise (`?.`, `!!`) everywhere. The cost: if you **read** the property before assigning it, Kotlin throws `kotlin.UninitializedPropertyAccessException: lateinit property X has not been initialized`. ## The `isInitialized` check `isInitialized` is a `val` defined as an extension on a **bound property reference** of a `lateinit` property. You access it through the `::` operator: ```kotlin class Repo { private lateinit var cache: Map<String, Int> fun warm(values: Map<String, Int>) { cache = values } fun lookup(key: String): Int? = if (this::cache.isInitialized) cache[key] else null } ``` - `this::cache` is a **bound property reference** (`KProperty0<Map<String, Int>>` bound to `this`). - `.isInitialized` returns `true` once `cache` has been assigned, `false` otherwise. - It avoids both the crash and an ugly `try { cache } catch (e: UninitializedPropertyAccessException) { ... }`. ## Hard constraints - **Only on `lateinit` properties.** A plain `val`/`var` reference has no `isInitialized`; it won't compile. - **Only from inside the declaring class** (or a subclass for accessible properties) — you reference the property via `this::prop` or `prop::name` patterns within scope. You cannot call `someOtherObject::prop.isInitialized` from outside; the reference must resolve to a `lateinit` you can see. - **Not for `val`, primitives, or nullable types.** `lateinit` itself forbids primitives and nullable types, so `isInitialized` inherits those limits. ## When to reach for it - Guarding a read in a method that might run before the init hook. - Idempotent lazy setup: `if (!this::client.isInitialized) client = build()`. - Avoiding `UninitializedPropertyAccessException` in teardown/`onDestroy` when init may have been skipped. Prefer `by lazy { }` when the value is computed once on first access and you never need the 'not yet set' state — `lazy` removes the need for `isInitialized` entirely.

  • What exception do you get if you read a lateinit property before assignment?
    kotlin.UninitializedPropertyAccessException, with a message naming the property.
  • Can you use isInitialized on a regular (non-lateinit) var?
    No. It is only available on a property reference to a lateinit property; otherwise it doesn't compile.

Like checking if a mailbox already has a letter before reaching in, instead of getting bitten because it's empty.

saying these in an interview costs you the question

  • Thinking isInitialized works on any nullable or ordinary property
  • Saying it checks for null (it checks assignment, not nullness)
  • Using a try/catch on UninitializedPropertyAccessException instead of the check
  • Believing reading an unset lateinit returns null instead of throwing

context

open as a page

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

level: juniorimportance: must knowfreq 80%

basics

~10 s

lateinit lets you declare a non-null property without giving it a value right away. You promise to set it later before using it, so you avoid making it nullable.

open as a page

What are the restrictions on what kinds of properties can be declared `lateinit`?

level: middleimportance: must knowfreq 70%

basics

~10 s

It must be a var, not a val. It can't be a nullable type, can't be a primitive like Int or Boolean, and can't have a custom getter or setter.

open as a page

What happens if you read a `lateinit var` before it is assigned, and what exception is thrown?

level: middleimportance: must knowfreq 65%

basics

~10 s

Reading it before you set it throws an error called UninitializedPropertyAccessException. The message tells you which property wasn't initialized.

open as a page

Why can you only call `isInitialized` through `this::prop` from inside the declaring class, and not on another object's lateinit property from outside?

level: middleimportance: should knowfreq 40%

basics

~20 s

The check needs a direct reference to that exact property, which Kotlin only gives you where the property is visible — normally inside the class that declares it. From outside you usually can't form that reference, so you can't call the check.

open as a page

When is `lateinit` + `isInitialized` the right tool versus a nullable property or `by lazy`?

level: middleimportance: should knowfreq 45%

basics

~20 s

Use lateinit+isInitialized when a non-null value is set later by a framework and you sometimes need to ask 'is it set yet?'. Use nullable when absence is a normal value. Use by lazy when the value is computed once on first use.

open as a page

What are the failure modes and edge cases of `isInitialized` — type restrictions, concurrency, and what it does NOT guarantee?

level: seniorimportance: should knowfreq 30%

basics

~20 s

It only works on lateinit (no val, primitives, or nullable types), only inside the declaring class, and only tells you 'has been assigned' — not 'currently valid'. Under threads, true now doesn't guarantee the value is still usable later.

open as a page

When would you choose `lateinit var` versus `by lazy`, and what are the key differences?

level: seniorimportance: should knowfreq 60%

basics

~10 s

Use by lazy for a val you compute yourself on first read. Use lateinit var for a non-null var that something else sets later, like dependency injection or test setup.

open as a page

Critique `lateinit var` for dependency injection: what are the design and safety trade-offs versus constructor injection?

level: principalimportance: nice to knowfreq 40%

basics

~10 s

lateinit for injection is convenient but moves the 'is it set?' guarantee from compile time to runtime. Constructor injection keeps dependencies non-null and verified at construction, which is safer and easier to test.

open as a page