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
- Primary Constructor and Property Parameters
- init Blocks and Initialization Order
- Custom Getters and Setters with field
- lateinit and Lazy Initialization