Подтвердить что ты не робот

Что такое семантика инициализации класса Kotlin?

Мне не удалось найти что-либо в определении языка, которое объясняет инициализацию класса в Котлин.

import java.util.Properties

fun main(args: Array<String>) {
    val out = MyClass()
    out.fn()
}

class MyClass {
    private val a = Properties() // 1

    init {
        fn()
    }

    public fun fn() {
        println("Fn called.  a = $a")
    }

    //  private val a = Properties() // 2
}

Результаты запуска этой программы изменяются в зависимости от того, инициализировано ли свойство в (1) или в (2).

Я удивлен, что порядок декларации имеет отношение к этому языку и хотел бы понять решения, стоящие за этим. Мое ожидание будет состоять в том, что свойства инициализируются до того, как будет вызван тело конструктора.

4b9b3361

Ответ 1

Мое ожидание будет состоять в том, что свойства инициализируются перед вызовом тела конструктора.

Ну, блок init не является конструктором. Это другой конструктор, который позволяет выполнять инициализацию объекта, и они [init blocks] выполняются в порядке объявления с инициализаторами свойств.

Конструкторы - это другой зверь ant, они выполняются после того, как все свойства были инициализированы, и все блоки init были выполнены. Посмотрите на следующий пример:

class A(val value: Int) {
    constructor(): this(0) {        
        println("Constructor")
    }    

    init {
        println("Init block")
    }
}

fun main(args: Array<String>) {
    val a = A()
}

Выход:

Init block  
Constructor

Вы можете разместить блок init везде, где хотите: до constructor или после него; он всегда будет выполняться перед конструктором A (вторичный конструктор, в этом примере).

Ответ 2

Проще говоря: при создании экземпляра класса (почти) сначала выполняется конструктор родительского класса (если он есть), а затем первичный конструктор.

Основной конструктор выполняет код, объявленный в корпусе класса сверху вниз. Также имена стали доступны по тому же правилу:

class Foo(a: String = "might be first"
          val b: String = "second" + a) : Boo(a + b + "third"){
    var c = a + "fourth" + b

    init {print("fifth: $c")}

    val d = "sixth"
    init {print("seventh: the end of the primary constructor"}
}

Если вы вызываете вторичный конструктор, то он работает после первичного, поскольку он составлен в цепочке (аналогично вызову родительских конструкторов).