假设我有一个类A和B,如下所示:
class A {
var x: Int { return 9 }
func p() { print(x) }
}
class B: A {
...
}
let v: A = B()
v.p()
然后我用三种不同的方式覆盖B中的x:
class B: A {
// OPTION 1: FINE - prints 10
override var x: Int { return 10 }
// OPTION 2: ERROR - cannot override with a stored property 'x'
override var x = 10
// OPTION 3: FINE - prints 10
override lazy var x = 10
}
有人能解释一下用存储属性覆盖计算属性有什么问题吗?为什么当我声明它是懒惰的时候,所有的东西都突然固定了?
2条答案
按热度按时间vh0rcniy1#
没有真正的技术原因可以解释为什么不能用存储的属性重写属性;它有getter和setter(访问器),可以覆盖要覆盖的属性的访问器。2它应该只是等价于用一个计算属性覆盖,然后转发到一个存储属性。
不过,我确实预见到允许这样做会有一个小小的困难:属性观察器重写。
请看下面的例子:
我们可以在
D
中重写f
的didSet
,在这种情况下,override var f: Int
本质上被视为一个计算属性,带有一个getter和setter,它转发到super
,并在setter中对didSet
实现进行额外的调用。这里
D
没有引入实际的 * storage *。那么为什么会有问题呢?那么,我们如何告诉编译器我们 * do * 实际上需要为f
存储?添加一个初始化表达式(= 10
)可以传达这一点,但不是所有存储的属性都有默认值;大多数都是从类的指定初始化器初始化的。我们可能需要某种属性,但是对于这样一个有限的用例,这看起来对语言来说不是一个特别有用的改变。lazy
的情况很清楚,因为我们不能向它们添加属性观察器。尽管如此,你所陈述的具体情况也应该是明确的;因为基本属性只有一个getter,所以没有属性观察器可以覆盖。我建议你file a bug(希望)看看Swift团队对此有什么看法。
使用计算属性重写(然后转发到存储属性)始终可以获得相同的结果,但:
kmb7vmvb2#
不能用可写存储属性重写只读计算属性。
具有getter但没有setter的计算属性称为只读计算属性。只读计算属性始终返回一个值,并且可以通过点语法访问,但不能设置为其他值。
文件