Home > Mobile >  kotlin parent class has var that depend on abstract var
kotlin parent class has var that depend on abstract var

Time:12-01

class C(val string: String) {
    init {
        println(string)
    }
}
abstract class A {
  abstract var string: String  
  val c = C(string)
}
class B : A() {
    override var string = "string"
}


fun main() {
    B()
}

kotlin playground for the problem

This code crash in runtime due to string var not initialized, how to do it right?

CodePudding user response:

It's not a good practice and dangerous to use an abstract or open variable in the initialization of your class. And if you write this code in Android Studio or IntelliJ IDEA you will get this warning: Accessing non-final property string in constructor.

So what's happening here ? Well the super class which is A is going to be initialized first before totally initializing B, so this line of code val c = C(string) is going to run before even giving a value to string and that's what causing the error and you will get a NullPointerException because string is null.

How to fix this ? You can use lazy to initialize c like that:

val c by lazy { C(string) }

Now c is not going to be initialized only if you call it, so now it's safe because you can't call it only if B is fully initialized.

CodePudding user response:

You are initialising A's properties using non-final properties - in this case, you are initialising c with the abstract property string.

abstract var string: String
val c = C(string)

This in general could be unsafe. Subclasses could override the non-final property in such a way that it is initialised at a later point, which means any initialisation that depends on the non-final property in the superclass will get an undefined value.

In this case, this is exactly what happens. B overrides string so that it is initialised after A's primary constructor is called. As a result, when A's primary constructor is run, and c is initialised, string has the value of null.

To fix this, you can either make c lazy:

val c by lazy { C(string) }

This will only initialise c when you first access it, with whatever the value of string is at that time.

Alternatively, make c computed:

val c get() = C(string)

This will make a new C every time you access c, with the current value of string.

  • Related