So I had some code that looked like this:
abstract class GenericField {
abstract val id: Int
abstract val data: ByteBuffer
}
open class OneByteFieldType : GenericField {
override val id: Int
override val data: ByteBuffer
constructor(buf: ByteBuffer) {
this.id = parseId(buf)
this.data = parseData(buf)
}
constructor(id: Int, data: ByteBuffer) {
this.id = id
this.data = data
}
}
And it warned me about “accessing non-final property in constructor” when I assign this.id
and this.data
in both the constructors, but, if I change the code to this:
abstract class GenericField {
abstract val id: Int
abstract val data: ByteBuffer
}
open class OneByteFieldType(
override val id: Int,
override val data: ByteBuffer
) : GenericField() {
constructor(buf: ByteBuffer) : this(parseId(buf), parseData(buf))
}
The warning is gone. But why? What about a primary constructor makes this problem go away? From what I can tell, it can still happen. Take this example:
abstract class Parent {
abstract val id: Int
}
open class Child1(
override var id: Int
) : Parent()
class Child2 : Child1 {
override var id: Int = 0
constructor(id: Int) : super(id) {
// this.id = id
}
fun parentId(): Int = super.id
}
fun main(args: Array<String>) {
val c2 = Child2(42)
println(c2.id)
println(c2.parentId())
// Prints:
// 0
// 42
}
When Child2
calls super, its id
field isn’t initialized yet so it uses the one in Child1
. Isn’t this the issue that the original warning (“accessing non-final property in constructor”) was alerting me about? Can the compiler just not tell that the same issue will happen here? Or is something actually different?