为什么我们不能将val属性覆盖为带有get而没有初始化的var?

问题描述 投票:3回答:2

我刚刚开始学习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中打印出来。

jvm kotlin
2个回答
3
投票

这种行为是由于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
            }

4
投票

如果没有以某种方式定义的访问器(varget),则无法定义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

© www.soinside.com 2019 - 2024. All rights reserved.