Почему компилятор Kotlin требует явного инициализатора свойства var?

Я не могу понять следующую часть документации Kotlin:

The initializer, getter and setter are optional. Property type is optional
if it can be inferred from the initializer or from the base class member being overridden.

Examples:
var allByDefault: Int? // error: explicit initializer required, default 
getter and setter implied

Единственное объяснение, почему компилятор требует здесь явного инициализатора (по крайней мере, единственное объяснение, которое я могу придумать) заключается в том, что у Kotlin нет значений свойств по умолчанию. Это правильно? Если да, то почему? Другими словами: в чем разница между свойствами Kotlin и полями Java (которые имеют значения по умолчанию), что не позволяет нам иметь значения свойств по умолчанию?

Ответ 1

Это просто: в значениях по умолчанию Java 0 (ноль) и null. Но в Kotlin большинство значений не являются нулевыми, поэтому вы не можете их инициализировать с помощью null. Для примитивных значений может существовать стратегия инициализации по умолчанию с нулями, но она не была выполнена, чтобы быть последовательной. Но в примитивных массивах значение по умолчанию равно нулю.

Если вам действительно нужна эта семантика инициализации, взгляните на свойства lateinit: https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties.

Этот механизм в принципе позволяет инициализировать поле с помощью null, но затем освобождает вас от нулевых утверждений.

Сложение

На самом деле Котлин очень умный умный об инициализации. Например, это работает:

val x: Int

if(something)
    x = 1
else
    x = 2

println(x)

Здесь kotlinc может предположить, что x инициализируется до его использования, поэтому код в порядке

Ответ 2

Котлин ничего не делает неявным образом. Он не преобразует числовые типы без вашей конкретной инструкции и не устанавливает значение по умолчанию или инициализацию без его явного. Это выбор дизайна для устранения распространенных ошибок, которые были обнаружены в типичных Java-программах. Неясно компилятору, если вы забыли инициализировать его или если вы предназначались для значения по умолчанию, которое будет использоваться. Поскольку это не ясно, это плохо. И поэтому, вероятно, это приводит к ошибкам.

Выбор дизайна Kotlin помогает устранить ошибки из-за кода, в котором компилятор не может определить, есть ли ошибка. Это философский и последовательный язык.

Kotlin требует инициализации перед использованием. Для членов, которые означают, что к тому времени, когда конструкторы и инициализаторы завершены, он должен иметь значение. Модификатор lateinit на var позволяет это игнорировать во время компиляции, хотя во время выполнения проверка выполняется при доступе к переменной. Для локальных переменных любая ветвь кода должна инициализировать значение перед доступом. Например:

fun stateFromAbbreviation(abbreviation: String?): String {
    val state: String

    if (abbreviation == null) {
        state = DEFAULT_STATE
    }
    else {
        state = stateMap.get(abbreviation) ?: throw IllegalStateException("Invalid state abbreviation $abbreviation")
    }

    return state
}

Здесь локальная переменная может быть инициализирована в выражении if, предполагая, что все ветки инициализируют значение. Но на самом деле этот код был бы более идиоматичным, используя if в качестве выражения, например:

fun stateFromAbbreviation(abbreviation: String?): String {
    return if (abbreviation == null) {
        DEFAULT_STATE
    }
    else {
        stateMap.get(abbreviation) ?: throw IllegalStateException("Invalid state abbreviation $abbreviation")
    }
}