What you'll learn
In Kotlin you work with properties, not raw fields. That distinction unlocks computed values, validation on assignment, and clean syntax — all while looking like a simple attribute. This module covers custom accessors, the backing field, and lateinit.
By the end you'll be able to:
- Understand properties vs fields
- Write computed properties with custom getters
- Validate assignment with custom setters and the
fieldkeyword - Defer initialisation with
lateinit
Properties, not fields
When you write val name: String, Kotlin creates a property — a field plus an automatic getter (and a setter for var). You access it like an attribute (p.name), but you can customise how it's read or written whenever you need to, without changing any code that uses it.
Computed properties
A property doesn't have to store a value — it can compute one each time it's read, by providing a custom get(). This is perfect for derived values like an area or a full name. There's no method-call syntax; it reads like a plain property:
class Circle(val radius: Double) {
// a COMPUTED property — no stored value, calculated on each access
val area: Double
get() = 3.14159 * radius * radius
val circumference get() = 2 * 3.14159 * radius
}
val c = Circle(2.0)
println(c.area) // 12.56636
println(c.circumference) // 12.56636Custom setters & the backing field
A custom set(value) lets you run code when a property is assigned — validation, logging, notifying observers. Inside the accessor, field is the backing field: the actual storage. You must use field (not the property name) to avoid infinite recursion:
class Temperature {
var celsius: Double = 0.0
set(value) {
field = value // 'field' is the backing field
println("Set to $value°C")
}
val fahrenheit get() = celsius * 9 / 5 + 32
}
val t = Temperature()
t.celsius = 25.0
println("${t.fahrenheit}°F")Use field, not the property name
celsius = value would call the setter again — infinite recursion. Always assign to field, which is the hidden storage the compiler generates for you.lateinit
Sometimes a non-null property can't be set at construction time — it's wired up later (a common pattern with dependency injection or Android views). lateinit var lets you declare a non-null property and promise to initialise it before first use, avoiding a nullable type:
class Service {
lateinit var name: String // "I'll set this before using it"
fun start(n: String) { name = n }
}
val s = Service()
s.start("Auth")
println(s.name) // AuthNote
lateinit property before it's set throws an UninitializedPropertyAccessException. Use it only when you're confident initialisation happens first; otherwise, a nullable type (String?) with the null-safety tools from Module 3 is safer. lateinit also can't be used with primitive types.Recap & quick check
Key takeaways
- Kotlin gives you properties (field + accessors), not raw fields — accessed like attributes.
- A custom get() creates a computed property, recalculated on each access, with no storage.
- A custom set(value) runs code on assignment; use 'field' (the backing field) to store the value.
- Assigning to the property name inside its own setter causes infinite recursion — use field.
- lateinit var declares a non-null property initialised later; it throws if accessed before being set.
Quick check
1. What does a custom get() with no backing field create?
2. Inside a custom setter, what does 'field' refer to?
3. Why must you use 'field' instead of the property name in a setter?
4. What is 'lateinit var' for?
5. What happens if you access a lateinit property before setting it?
Great — your properties can now compute and validate. Next up: Module 15 — Data Classes.