Что такое семантика инициализации класса 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).

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

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

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

Конструкторы – это другой зверь, и они выполняются после того, как все свойства были инициализированы, и все блоки 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 (вторичный конструктор в этом примере).

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

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

 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"} } 

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