I haven't been able to find anything in the language definition that explains the initialisation of a class in 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
}
The results of running this program change depending whether the property is initialised at (1) or at (2).
I'm surprised that the declaration order is relevant in the language and would like to understand the decisions behind this. My expectation would be that properties are initialised before the constructor body is invoked.
My expectation would be that properties are initialised before the constructor body is invoked.
Well, init
block is not a constructor. It is a different construct which allows you to perform the initialization of the object and they [init blocks] are performed in the declaration order with the property initializers.
Constructors are a different beast ant they are performed after all the properties were initialized and all init blocks were performed. Look at the following example:
class A(val value: Int) {
constructor(): this(0) {
println("Constructor")
}
init {
println("Init block")
}
}
fun main(args: Array<String>) {
val a = A()
}
Output is:
Init block
Constructor
You can place the init
block wherever you want: before the constructor
or after it; it will always be performed before the A
's constructor (secondary constructor, in this example).
Simply put: when an instance of a class is created, (almost) firstly runs the constructor of the parent class (if present), then the primary constructor.
The primary constructor executes code declared in the class body from the top to the bottom. Also the names became available by the same rule:
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"}
}
If you invoke a secondary constructor, then it works after the primary one as it is composed in the chain (similar to invoking the parent constructors).