Home Articles

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

Asked
Viewed 1868 times
3

我刚刚开始学习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}")
}

如果我没有在 Bar 类中初始化 x ,则会给出编译器错误 Property must be initialised . 但是当我打印 bar.x 时,它打印从重写的getter计算的值,但不打印初始化的getter .

2 Answers

  • 4

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

    如果没有以某种方式定义的两个访问器( getset ),则无法定义 var 属性 .

    放置初始化程序( 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

Related