Kotlin parent logic to run after child's constructor

Viewed 375

I got the following classes:

class B() : A {
    override val p1: String = ...
    override fun p2() : String { ... }
}

abstract class A() {
    abstract val p1: String
    abstract fun p2() : String
    fun p3() : String = p1.plus("-").plus(p2())
    init {
       var p4 = p3()
    }

}

I want p4 to be set at declaration. the issue is that when A() constructor is called p1 & p2() are not set yet, so p3() contains nulls. is there a way to solve it? like to run some logic after both constructors are called?

2 Answers

The function inheritance won't have any problem, as functions resolve statically.

Properties on the other hand, initialize after init block of super class has been executed.

You can simply fix this by moving the property p1 to the A's constructor instead, and add that to the constructor argument when extending the class:

class B : A(p1 = "...") {         /* `p1 = ` is optional */
    override fun p2(): String { return "asdf" }
}

abstract class A(val p1: String) {
    abstract fun p2(): String
    fun p3() : String = p1.plus("-").plus(p2())
    init {
        var p4 = p3()
        println(p4)  // prints: `...-asdf`
    }
}

Easiest way is probably to just make it a lazy property that gets computed when it's first accessed (after construction)

abstract class A() {
    abstract val p1: String
    abstract fun p2() : String
    fun p3() : String = p1.plus("-").plus(p2())
    var p4: String by lazy { p3() }
}

If you actually want to use p4 in the init block though, i.e. you're doing something with it as part of the construction process, and you just want it to run after B has been constructed and initialised, I don't think there's any way around that. You might want to do some kind of getInstance approach instead

class B private constructor () : A() {
    override val p1: String = "P1"
    override fun p2() : String { return "P2" }

    companion object {
        fun newInstance() = B().apply{ initialise() }
    }
}

abstract class A() {
    abstract val p1: String
    abstract fun p2() : String
    fun p3() : String = p1.plus("-").plus(p2())

    fun initialise() {
        var p4 = p3()
        print(p4)
    }
}

but honestly the best way might just be to pass p1 and p2 into A as parameters, especially if it's a data class anyway

Related