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
.