Почему этот класс печатает files=0, хотя limit равен 3?
Loader строит список файлов в инициализаторе свойства и печатает его размер из блока init. Значение limit равно 3, но напечатанный размер — 0. Компилятор не выдаёт ни ошибки, ни предупреждения.
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") } // печатает files=0
Определите причину и исправьте.
Инициализаторы свойств и блоки init выполняются сверху вниз, в порядке объявления, как единое тело первичного конструктора. files = load() выполняется до присваивания limit, поэтому load() читает значение по умолчанию 0 и строит пустой список. Компилятор этого не видит, так как чтение идёт через вызов функции. Решение — объявить limit выше files.
- ✗Считать, что блоки
initвыполняются после всех инициализаторов, а не вперемежку с ними - ✗Ждать, что компилятор поймает опережающее чтение, идущее через вызов функции
- ✗Думать, что
valдоступен с момента открытия тела класса
- →Какое значение хранит ещё не инициализированное свойство
Int, пока конструктор работает? - →Как
by lazyнаfilesтоже чинит это без перестановки объявлений?
Баг
Инициализаторы свойств и блоки init — это не отдельные шаги: компилятор сшивает их в одно тело первичного конструктора и выполняет строго в порядке объявления, сверху вниз.
class Loader(private val path: String) {
private val files = load() // 1) выполняется первым
init { println("files=${files.size}") } // 2)
private val limit = 3 // 3) присваивается последним
private fun load(): List<String> = List(limit) { "$path/$it" }
}
К моменту вызова load() поле limit ещё не присвоено и хранит значение по умолчанию для Int — 0. List(0) { ... } даёт пустой список, поэтому печатается files=0.
Прямое опережающее чтение (val a = b; val b = 1) компилятор ловит. Здесь чтение спрятано за вызовом функции, поэтому диагностики нет.
Исправление
Объявите limit выше зависящего от него свойства:
class Loader(private val path: String) {
private val limit = 3 // ✅ инициализирован первым
private val files = load()
init { println("files=${files.size}") } // files=3
private fun load(): List<String> = List(limit) { "$path/$it" }
}