androidkotlininit

Parameter in subclass without init-block is null although parameter was non-null


I have two classes that control buttons in Android.

SortingButton class for a simple Button with which you can choose ascending/descending sort

open class SortingButton(
    protected val context: Context?,
    private val binding: FragmentDualSortingBinding,
    private var orderState: SortingOrderEnum,
    protected val button1: MaterialButton,
    protected val button2: MaterialButton) {
    // this class cares for the two buttons on the left
    // only for the importance

    init {
        this.onCreate()
  
    }

    open fun onCreate() {
         ...
    }
}

ComplexSortingButton with two buttons. One for type of sort, one for ascending/descending

class ComplexSortingButton(
    context: Context?,
    binding: FragmentDualSortingBinding,
    orderState: SortingOrderEnum,
    private var evaluator : SecondaryEvaluator?,
    button1: MaterialButton,
    button2: MaterialButton
) : SortingButton(context, binding, orderState, button1, button2) {

    init {
        onCreate()
    }

    override fun onCreate() {
        Log.d("ComplexSortingButton", "onCreate - evaluator: $evaluator")
        super.onCreate()
        
    }
}

Now when I pass a non-null argument evaluator to ComplexSortingButton I receive evaluator as null.

I have a solution. But I don't know why it works.

When placing an init-block in ComplexSortingButton, evaluator is no longer null when received.

init {
    this.onCreate()
}

Why?


Solution

  • You should never call an open function from the constructor of a class because it can result in inconsistent/unexpected state, null pointer exceptions, etc. I’m surprised Kotlin even lets you do it without a compile error, but it does at least show a compile warning. By “from the constructor”, I mean in any property initializer expressions or init blocks because these are all called at class construction time.

    In this case, you are calling the open function onCreate(). from the superclass constructor, so it is called before anything in the subclass constructor, which is why you have an unexpected null value if you try to access subclass properties in the subclass’s implementation of onCreate.

    Your workaround hack might be causing the compiler to change some order of events during class construction that is making your property value available. I would not rely on this because it’s not guaranteed behavior and could change in future compiler versions.

    Currently, your open onCreate() function isn’t doing anything useful. You could simply remove it and use init blocks normally in the superclass and subclass.