There’s been something I’ve been struggling with regarding Kotlin constructors.
Kotlin constructors, just like in Java, are invoked after initialization.
That is:
class Foo {
constructor() {
println(t) // 3
}
val t: Int = 3
}
According to the docs, “initializer blocks effectively becomes part of the primary constructor.” However, there’s a pretty big difference. Initializer blocks happen during initialization, not after construction, so you can’t invoke open methods, reference a leaky this
, refer to open properties, etc.
The problem I hit is that you can’t declare a val
in a secondary constructor (makes sense) but you also can’t write a block for the primary constructor.
class Foo {
// val on secondary constructor parameter not allowed
constructor(val f: Int) {
println(f)
println(t)
}
val t: Int = 3
}
In that example it is confusing that the constructor is considered secondary, especially given that you can’t construct Foo as Foo()
To give a real use case, let’s take an example from the kotlin constructor documentation:
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
This example breaks down if you wanted parent
to be a required val.
Often you can work around the issues I’m talking about by using a private primary constructor and public secondary, but then you hit a problem with overload ambiguity.
Example:
class Foo private constructor(val f: Int) {
// Overload resolution ambiguity
constructor(f: Int) : this(f) {
println(f)
println(t)
}
val t: Int = 3
}
A proposed solution, which I could have sworn existed in the early days of Kotlin…
I would like the ability to do the following:
open class Foo {
primary constructor(val f: Int) : Bar() {
println(f)
println(t) // Possibly overridden value
}
open val t: Int
init {
t = 3
}
}