Why does this fix the "accessing non-final property in constructor" warning?


#1

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?