我刚刚开始学习Kotlin。我想知道为什么我们不能在重写的val属性上使用get()
方法作为var而不进行初始化。但当被覆盖为val
时,它可以在没有初始化的情况下工作。
open class Foo {
val y = 21
open val x: Int
get() {
return 10 * y
}
}
class Bar : Foo() {
override var x: Int = super.x * 10 //If overridden as val works without initialisation
get() {
return super.x * (super.x * 10)
}
}
fun main(args: Array<String>) {
val bar: Bar = Bar()
println("${bar.x}")
}
如果我没有在x
类中初始化Bar
,则会给出编译器错误Property must be initialised
。但是当我打印bar.x
时,它会打印从重写的getter计算出的值,但不会从初始化的getter中打印出来。
这种行为是由于Kotlin中backing fields的概念。正如文档中所写 -
如果属性使用至少一个访问器的默认实现,或者自定义访问者通过字段标识符引用它,则将为该属性生成支持字段。
由于重写的属性x
现在是一个var,它有一个默认的setter实现,如下所示。
override var x: Int = super.x * 10
get() {
return super.x * (super.x * 10)
}
set(value) {
field = value
}
因此,在声明var时,必须使用某个值初始化支持字段,因为Kotlin中没有默认值的概念(例如,未初始化的Java对象采用空值)。
另一个解决方案是使用这样的自定义setter -
override var x: Int // Now you can leave it uninitialized
get() {
return super.x * (super.x * 10)
}
set(value) {
// Nothing happens
}
如果没有以某种方式定义的访问器(var
和get
),则无法定义set
属性。
放置初始化程序(var x: Int = ...
)时,会生成一个支持字段和一个设置支持字段的默认设置器。初始化程序的替代方法是提供自定义setter:
override var x: Int
get() = super.x * (super.x * 10)
set(value) { super.x = sqrt(value / 10.0).roundToInt() }
请参阅:语言参考中的Properties and Fields