2

This is the code snippet:

abstract class SuperClass {
    init {
        toOverride()
    }

    abstract fun toOverride()
}

class ChildClass : SuperClass() {
    private val innerClass = InnerClass()

    override fun toOverride() {
        innerClass.doSomething()
    }

    class InnerClass {
        fun doSomething() = Unit
    }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val superClass = ChildClass()
        superClass.toOverride()
    }
}

Calling superClass.toOverride() will cause null pointer exception but from ChildClass point of view innerClass is an immutable field. The problem is that in the constructor of SuperClass toOverride() is being called.

We don't have control over how SuperClass is written in a lot of cases. In android framework there is a lot of such cases, for example http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/android/view/View.java#20160 will be called in the constructor of View.java. How do we address those issues? Adding null checks will trigger IDE warning and looks like kotlin compiler will trim the null check sometimes if you use ?. operator since it think the ?. is not necessary.

darklord
  • 5,077
  • 12
  • 40
  • 65
  • 5
    There's pretty good explanation [here](https://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors), and it's not exclusive to Kotlin. – Pawel Jul 26 '18 at 22:19

1 Answers1

1

To start with, I'd read What's wrong with overridable method calls in constructors?

With that out of the way, and if you really can't control what the parent class does and you really really need it to do what it's doing, you can probably do something like this (Only showing the ChildClass:

class ChildClass : SuperClass() {
    private val innerClass: InnerClass? = InnerClass()

    init {
        // beware: this is ONLY done because toOverride ONLY calls
        // innerClass.doSomething(), you have to make sure that anything else
        // there is idempotent
        toOverride()
    }

    override fun toOverride() {
        innerClass?.doSomething()
    }
}

My recommendation would be to think if there's an alternative implementation to your class hierarchy.

marianosimone
  • 3,366
  • 26
  • 32
  • Changing your val into nullable just to violate object construction seems like a bad idea from the start, not mentioning you'd need to add nullability checks everywhere just for that. You also cannot prevent error on delegated fields like `lazy`. – Pawel Jul 26 '18 at 23:12
  • Calling an open method from a constructor seems like a bad idea from the start... This is just to get around that problem if and only if there's no alternative to the current hierarchy. `lazy` doesn't work in this case either, because the `SuperClass` constructor is called before the delegate assignment is made, so you end up with the same NPE (I tried that before proposing this) – marianosimone Jul 26 '18 at 23:20