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 problem
这个代码崩溃在运行时由于字符串var未初始化,如何做正确的?
在类的初始化中使用抽象变量或开放变量不是一个好的做法,而且很危险。如果你在Android Studio或IntelliJ IDEA中编写这段代码,你会得到这样的警告:Accessing non-final property string in constructor
.
这里发生了什么?在完全初始化B
之前,超类A
将首先被初始化,因此val c = C(string)
这行代码将在给string
赋值之前运行,这就是导致错误的原因,您将得到NullPointerException
,因为string
是null
。
如何解决这个问题?你可以使用lazy
像这样初始化c:
val c by lazy { C(string) }
现在c
不会被初始化只有当你调用它,所以现在它是安全的因为只有在B
完全初始化时才能调用它
您正在使用非final属性初始化A
的属性-在本例中,您正在使用abstract
属性string
初始化c
。
abstract var string: String
val c = C(string)
这通常是不安全的。子类可以覆盖非final属性,这样它就可以在稍后初始化,这意味着任何依赖于超类中的非final属性的初始化都将得到一个未定义的值。
在这种情况下,这正是所发生的。B
覆盖string
,因此在A
的主构造函数被调用之后,它被初始化。因此,当A
的主构造函数运行时,c
被初始化,string
的值为null。
要解决这个问题,您可以将c
设置为lazy:
val c by lazy { C(string) }
这只会在你第一次访问c
时初始化它,不管string
的值是多少。
或者,让c
computed:
val c get() = C(string)
这将在每次访问c
时生成一个新的C
,当前值为string
。