What is `lateinit var` in Kotlin, and what problem does it solve?
answer
- Deferred init for non-null var
- Avoids nullable + ?./!!
- DI, @BeforeEach, onCreate
- Throws UninitializedPropertyAccessException
- Skips compile-time init check
basics
~10 slateinit 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.
solid answer
~30 s`lateinit var` marks a non-null, mutable property whose initialization is deferred to after the constructor runs. Without it, a non-null property must be assigned in the constructor or at declaration; the alternative is making it nullable (`T?`) and littering code with `?.`/`!!`. It is meant for cases where a framework assigns the value later — dependency injection (`@Autowired`, `@Inject`), JUnit `@BeforeEach` setup fields, or Android `onCreate`. The compiler skips its usual must-be-initialized check, and instead inserts a runtime guard: accessing the property before assignment throws `UninitializedPropertyAccessException`. So you trade a compile-time guarantee for cleaner non-null typing plus a clear runtime failure.
code
kotlin · 12 linesclass Greeter {
lateinit var name: String // non-null, set later
fun configure(n: String) { name = n }
fun greet() = "Hello, $name" // no ?. or !! needed
}
val g = Greeter()
// g.greet() // would throw UninitializedPropertyAccessException
g.configure("Ada")
println(g.greet()) // Hello, Adago deeper
Can state lateinit defers initialization of a non-null var and is set later by a framework or setup code.
Explains the alternative (nullable + ?./!!), names DI and @BeforeEach use cases, and knows early access throws UninitializedPropertyAccessException.
Frames it as trading a compile-time definite-assignment guarantee for clean non-null typing plus a clear runtime guard, and enumerates the restrictions.
Discusses team conventions for when lateinit is acceptable vs constructor injection, and the maintainability trade-off of moving a guarantee to runtime.
## What `lateinit var` is `lateinit` is a modifier on a `var` property declaration. It tells the compiler: "this property is non-null, but I will not initialize it at the declaration site or in the constructor — I will assign it later, before any read." Normally Kotlin enforces *definite assignment*: a non-null property (`var name: String`) must have a value by the time the constructor finishes. `lateinit` opts out of that compile-time check. ```kotlin class UserService { lateinit var repository: UserRepository // no initializer needed fun init(repo: UserRepository) { repository = repo // assigned later } fun load(id: Long) = repository.find(id) // used as a plain non-null type } ``` ## The problem it solves Without `lateinit`, your two options are: - Initialize in the constructor — impossible when a framework supplies the value afterwards. - Make it nullable: `var repository: UserRepository? = null`. Then every use needs `?.` (safe call), `!!` (not-null assertion), or a null check, even though logically the value is always present after setup. `lateinit` keeps the **non-null type** (no `?`) so call sites stay clean, while moving the "is it set?" responsibility to runtime. ## Runtime behavior The compiler backs a `lateinit` property with a field that starts as `null` (internally) and inserts a check on every read. Reading it before assignment throws: ``` kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized ``` This is a clear, specific failure — unlike a generic `NullPointerException`. ## Typical use sites - **Dependency injection**: Spring `@Autowired`, Jakarta `@Inject`, Dagger field injection set the field after construction. - **Test fixtures**: a field set in a JUnit 5 `@BeforeEach` method. - **Android**: `View`s or bindings assigned in `onCreate`/`onViewCreated`. ## Key restrictions (named here, detailed elsewhere) - Must be `var`, not `val`. - Cannot be a primitive type like `Int`, `Long`, `Double`, `Boolean`. - Cannot be nullable, must have no custom getter/setter, and works on top-level/member/local properties (not constructor parameters). You can check assignment with `::repository.isInitialized` (covered separately).
- Why use `lateinit` instead of just initializing to a default like an empty string?A default masks the 'not configured yet' state — code silently uses the placeholder. `lateinit` makes premature access fail loudly, surfacing setup bugs instead of hiding them.
- Does `lateinit` make the property nullable?No. The declared type stays non-null (e.g., `String`, not `String?`). Internally the field may hold null before assignment, but you cannot assign null to it from Kotlin.
Like reserving a seat with a name tag but no person yet — the seat is 'for a real guest' (non-null), and if someone tries to talk to the empty chair early, you get a clear 'nobody's here yet' error.
saying these in an interview costs you the question
- Saying lateinit can be used on `val`
- Claiming it makes the type nullable
- Thinking early access returns null instead of throwing
- Confusing it with `by lazy`
- Saying it works on primitive Int/Boolean