用存储属性重写计算属性



假设我有一个类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
}

有人能解释一下用存储属性重写计算属性有什么错吗?为什么当我宣布它为惰性时,一切都突然修复了?

不能用存储的属性重写属性没有真正的技术原因;它有getter和setter(访问器),它们可以覆盖要覆盖的属性的访问器。它应该相当于用计算的属性重写,然后转发到存储的属性。

然而,我确实预见到允许这样做会有一个轻微的复杂性:属性观察者覆盖。

考虑以下示例:

class C {
var f: Int {
get { return 5 }
set { print("C says f was set") }
}
}
class D : C {
override var f: Int {
didSet {
print("D says f was set")
}
}
}
let d = D()
print(d.f)
d.f = 7
print(d.f)
// 5
// C says f was set
// D says f was set
// 5

我们可以在D中覆盖fdidSet。在这种情况下,override var f: Int本质上被视为一个计算属性,具有一个getter和setter,它转发到super上,并在setter中对didSet实现进行额外调用。

D在这里没有引入实际的存储。那么,为什么这会有问题呢?那么,我们如何告诉编译器我们确实想要f的存储空间呢?添加初始化器表达式(= 10)可以传达这一点,但并非所有存储的属性都有默认值;大多数是从类的指定初始化程序初始化的。我们可能需要某种属性,但对于这样一个有限的用例,这似乎不是对语言特别有用的更改。

lazy的情况是明确的,因为我们不能向它们添加属性观测器。

尽管如此,你提出的特定案例也应该清楚;由于基本属性只有一个getter,因此没有可重写的属性观测器。我建议你提交一个bug,(希望)看看Swift团队对此有什么看法。

您总是可以通过计算属性覆盖来获得相同的结果,然后转发到存储的属性上:

class C {
var f: Int {
return 9
}
}
class D : C {
private var _f: Int = 10
override var f: Int {
get { return _f }
set { _f  = newValue }
}
}
let d = D()
print(d.f) // 10
d.f = 7
print(d.f) // 7

您不能用可写存储属性覆盖只读计算属性。

一个有getter但没有setter的计算属性被称为只读计算属性。只读计算属性总是返回一个值,可以通过点语法访问,但不能设置为其他值。

单据

最新更新