Kotlin父类具有依赖于抽象var的var

f0brbegy  于 2022-12-04  发布在  Kotlin
关注(0)|答案(2)|浏览(167)
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
这段代码在运行时崩溃,因为字符串变量没有初始化,如何做正确的?

pcww981p

pcww981p1#

在类的初始化中使用抽象或开放变量不是一个好的做法,也是危险的。如果您在Android Studio或IntelliJ IDEA中编写此代码,您将收到以下警告:Accessing non-final property string in constructor .
这里发生了什么,超类A在完全初始化B之前,会先被初始化,所以这行代码val c = C(string)会在给string赋值之前运行,这就是导致错误的原因,你会得到一个NullPointerException,因为stringnull
你可以使用lazy来初始化c,如下所示:

val c by lazy { C(string) }

c只有在被调用时才被初始化,所以现在它是安全的,因为只有当B被完全初始化时才能调用它。

vc9ivgsu

vc9ivgsu2#

您正在使用非final属性初始化A的属性-在本例中,您正在使用abstract属性x1m3 n1 '初始化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设为已计算:

val c get() = C(string)

这将在每次访问c时生成一个新的C,当前值为string

相关问题