```open class Inheritance { open val xs: Mutab...
# getting-started
m
Copy code
open class Inheritance {
    open val xs: MutableList<Int> = mutableListOf()
}

class Inheritor: Inheritance() {
    override val xs: MutableList<Int> = mutableListOf(0)
}
Instantiating
Inheritor
doesn’t allocate twice for
xs
because that doesn’t initialise
super.xs
, right?
m
It does, and that's one of the nasty puzzlers in open properties with backing fields.
In general, the "normal" patterns are: • backing field in base + override properties with getters/setters in derived • abstract base + override properties with backing fields in derived
That avoids the problem of "I'm having a stored state in two places"
m
@Marat Akhin But why?
Copy code
fun main() {
    open class Super {
        open val x: Unit = Unit
        init { println(this.x) }
    }

    object: Super() {
        override val x = Unit
    }
}
The field is uninitialised but the
mutableListOf
function is still called?
Copy code
fun main() {
    open class Super {
        open val x: Unit = myFactoryFunc()
        init { println(this.x) }
    }

    object: Super() {
        override val x = myFactoryFunc()
    }
}

fun myFactoryFunc(): Unit {
    println("myFactoryFunc")
}
Oh wow, it actually does.
m
There are two fields in your example, and you're accessing one of them via the (overridden) getter.
And the one your getter accesses is the yet uninitialized one from the derived class.
If we take a step back, that code pattern is another problem; you've encountered what is known as "leaking this" (and that's an old one).
m
Oh if we were to pretend everything was a method for a second for a simpler mental model, you’re saying that the call to the getter is despatched to the subtype instance
m
Yes, and you're actually not pretending. A property read access is a getter call.