Why does this class print files=0 when limit is 3?
Loader builds its file list in a property initializer and reports the size from an init block. limit is clearly 3, yet the printed size is 0. The compiler reports no error and no warning.
class Loader(private val path: String) {
private val files = load()
init { println("files=${files.size}") }
private val limit = 3
private fun load(): List<String> = List(limit) { "$path/$it" }
}
fun main() { Loader("/tmp") } // prints files=0
Diagnose the cause and fix it.
Property initializers and init blocks run top to bottom, in declaration order, as one primary-constructor body. files = load() runs before limit is assigned, so load() reads limit's default 0 and builds an empty list. The compiler cannot see it because the read goes through a function call. Fix: declare limit above files.
- ✗Assuming
initblocks run after all property initializers rather than interleaved with them - ✗Expecting the compiler to catch a forward read that goes through a function call
- ✗Thinking a
valis available from the moment the class body opens
- →What value does an uninitialized
Intproperty hold while the constructor is still running? - →How would
by lazyonfilesalso fix this without reordering the declarations?
The bug
Property initializers and init blocks are not separate phases: the compiler stitches them into one primary-constructor body and runs them strictly in declaration order, top to bottom.
class Loader(private val path: String) {
private val files = load() // 1) runs first
init { println("files=${files.size}") } // 2)
private val limit = 3 // 3) assigned last
private fun load(): List<String> = List(limit) { "$path/$it" }
}
By the time load() runs, limit has not been assigned and still holds the JVM default for Int — 0. List(0) { ... } yields an empty list, so it prints files=0.
A direct forward read (val a = b; val b = 1) is rejected by the compiler. Here the read hides behind a function call, so there is no diagnostic.
The fix
Declare limit above the property that depends on it:
class Loader(private val path: String) {
private val limit = 3 // ✅ initialized first
private val files = load()
init { println("files=${files.size}") } // files=3
private fun load(): List<String> = List(limit) { "$path/$it" }
}