Constructors, init, and properties
Primary constructor
class User(val id: Long, var name: String)
id and name are constructor parameters and properties because they use val/var. id cannot be reassigned; name can.
init: run validation during construction
class Percentage(val value: Int) {
init {
require(value in 0..100) { "Percentage must be 0..100" }
}
}
Order of construction follows source order: property initializer, init block, next property initializer, next init block.
1 through 4 in source order.Secondary constructor
class User(val id: Long, val name: String) {
constructor(id: Long) : this(id, "Unknown")
}
Every secondary constructor must eventually delegate to the primary constructor. Prefer default arguments or named factory functions when they explain intent better.
Custom getter and setter
class Temperature(celsius: Double) {
var celsius: Double = celsius
set(value) {
require(value >= -273.15)
field = value
}
val fahrenheit: Double
get() = celsius * 9 / 5 + 32
}
field refers to the property's backing field. Calling celsius = value inside its setter would call the setter again forever.
32.0, followed by 212.0.lateinit and lazy
lateinit varpromises a non-null property will be assigned before reading. Reading too early throwsUninitializedPropertyAccessException.val by lazy { ... }computes once on first access and then stores the result.
✕ Avoid lifecycle-unsafe lateinit access
lateinit var token: String
println(token) // crashes if assignment has not happened
✓ Prefer constructor injection when the value is required
class ApiClient(private val token: String)
The primary constructor defines the main creation path. Property initializers and init blocks execute in source order. Secondary constructors delegate to the primary constructor. Use lateinit only when lifecycle-driven delayed assignment is unavoidable; use lazy for one-time computation on first access.