覆盖 Swift 中的存储属性
Posted
技术标签:
【中文标题】覆盖 Swift 中的存储属性【英文标题】:Overriding a stored property in Swift 【发布时间】:2014-12-28 19:01:16 【问题描述】:我注意到编译器不允许我用另一个存储值覆盖一个存储属性(这看起来很奇怪):
class Jedi
var lightSaberColor = "Blue"
class Sith: Jedi
override var lightSaberColor = "Red" // Cannot override with a stored property lightSaberColor
但是,我可以使用计算属性来执行此操作:
class Jedi
let lightSaberColor = "Blue"
class Sith: Jedi
override var lightSaberColor : Stringreturn "Red"
为什么我不能给它另一个值?
为什么用存储的属性覆盖是可憎的,而用计算出来的 kosher 来做呢?他们在想什么?
【问题讨论】:
Overriding superclass property with different type in Swift 的可能重复项 【参考方案1】:为什么我不能只给它另一个值?
您绝对可以为继承的属性赋予不同的值。如果你在构造函数中初始化属性,并从派生类传递一个不同的值,你就可以做到这一点:
class Jedi
// I made lightSaberColor read-only; you can make it writable if you prefer.
let lightSaberColor : String
init(_ lsc : String = "Blue")
lightSaberColor = lsc;
class Sith : Jedi
init()
super.init("Red")
let j1 = Jedi()
let j2 = Sith()
println(j1.lightSaberColor)
println(j2.lightSaberColor)
覆盖一个属性与给它一个新值不同——它更像是给一个类一个不同的属性。事实上,当您覆盖计算属性时会发生这种情况:计算基类中的属性的代码替换为计算派生类中该属性的覆盖的代码。
[是否] 可以覆盖实际存储的属性,即具有其他行为的
lightSaberColor
?
除了观察者,存储的属性没有行为,所以真的没有什么可以覆盖的。通过上述机制,可以为属性赋予不同的值。这正是问题中的示例试图实现的目标,但语法不同。
【讨论】:
@MaxMacLeod 除了观察者,存储的属性没有行为,所以真的没有什么可以覆盖的。他想在子类中给存储属性一个不同的值,但他不确定实现它的机制。答案解释了如何在 Swift 中完成。很抱歉回复晚了,看来您的评论引起了足够的混乱以吸引反对票,所以我决定解释一下发生了什么。【参考方案2】:您可能想要为该属性分配另一个值:
class Jedi
var lightSaberColor = "Blue"
class Sith: Jedi
override init()
super.init()
self.lightSaberColor = "Red"
【讨论】:
上述评论同样适用 init() 在 UIViewController 的子类上只是坐在 viewDidLoad【参考方案3】:对我来说,您的示例在 Swift 3.0.1 中不起作用。
我在操场上输入了这段代码:
class Jedi
let lightsaberColor = "Blue"
class Sith: Jedi
override var lightsaberColor : String
return "Red"
在 Xcode 中编译时抛出错误:
不能用 'var' 的 getter 覆盖不可变的 'let' 属性 'lightsaberColor'
不,您不能更改存储属性的类型。 Liskov 替换原则强制您允许在需要超类的地方使用子类。
但如果将其更改为var
并因此在计算属性中添加set
,则可以使用相同类型的计算属性覆盖存储的属性。
class Jedi
var lightsaberColor = "Blue"
class Sith: Jedi
override var lightsaberColor : String
get
return "Red"
set
// nothing, because only red is allowed
这是可能的,因为从存储属性切换到计算属性是有意义的。
但是用存储的var
属性覆盖存储的var
属性没有意义,因为您只能更改该属性的值。
但是,您根本不能用存储属性覆盖存储属性。
我不会说西斯是绝地:-P。因此很明显这是行不通的。
【讨论】:
【参考方案4】:class SomeClass
var hello = "hello"
class ChildClass: SomeClass
override var hello: String
set
super.hello = newValue
get
return super.hello
【讨论】:
虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。【参考方案5】:对于 Swift 4,来自Apple's documentation:
您可以覆盖继承的实例或类型属性以提供 您自己的该属性的自定义 getter 和 setter,或添加 属性观察者使覆盖属性能够观察何时 基础属性值发生变化。
【讨论】:
【参考方案6】:您也可以使用函数来覆盖。不是直接回答,但可以丰富这个话题)
A类
override func viewDidLoad()
super.viewDidLoad()
if shouldDoSmth()
// do
public func shouldDoSmth() -> Bool
return true
B 类:A
public func shouldDoSmth() -> Bool
return false
【讨论】:
【参考方案7】:不幸的是,在 Swift 中这是不可能的。最好的选择如下:
class Jedi
private(set) var lightsaberColor = "Blue"
class Sith: Jedi
override var lightsaberColor : String
get
return "Red"
【讨论】:
【参考方案8】:我在为视图控制器设置常量时遇到了同样的问题。
由于我正在使用界面生成器来管理视图,因此我无法使用 init()
,因此我的解决方法与其他答案类似,只是我在基类和继承类上都使用了只读计算变量。
class Jedi
var type: String
get return "Blue"
class Sith: Jedi
override var type: String
get return "Red"
【讨论】:
【参考方案9】:如果你尝试在 Swift 5 中这样做,你会看到一个
不能用 'var' 的 getter 覆盖不可变的 'let' 属性 'lightSaberColor'
最好的办法是将其声明为计算属性。
这有效,因为我们只是覆盖了get
函数
class Base
var lightSaberColor: String "base"
class Red: Base
override var lightSaberColor: String "red"
【讨论】:
【参考方案10】:Swift 不允许您覆盖变量 stored property
取而代之的是,您可以使用 computed property
class A
var property1 = "A: Stored Property 1"
var property2: String
get
return "A: Computed Property 2"
let property3 = "A: Constant Stored Property 3"
//let can not be a computed property
func foo() -> String
return "A: foo()"
class B: A
//now it is a computed property
override var property1: String
set
get
return "B: overrode Stored Property 1"
override var property2: String
get
return "B: overrode Computed Property 2"
override func foo() -> String
return "B: foo()"
//let can not be overrode
func testPoly()
let a = A()
XCTAssertEqual("A: Stored Property 1", a.property1)
XCTAssertEqual("A: Computed Property 2", a.property2)
XCTAssertEqual("A: foo()", a.foo())
let b = B()
XCTAssertEqual("B: overrode Stored Property 1", b.property1)
XCTAssertEqual("B: overrode Computed Property 2", b.property2)
XCTAssertEqual("B: foo()", b.foo())
//B cast to A
XCTAssertEqual("B: overrode Stored Property 1", (b as! A).property1)
XCTAssertEqual("B: overrode Computed Property 2", (b as! A).property2)
XCTAssertEqual("B: foo()", (b as! A).foo())
与Java相比更清楚,其中类字段不能被覆盖并且不支持多态性,因为它是在编译时定义的(高效运行)。它被称为变量隐藏[About]不建议使用此技术,因为它很难阅读/支持
[Swift property]
【讨论】:
【参考方案11】:您可以使用此方法更新值
class A
var lightSaberColor : String = "Red"
您不需要覆盖变量。您可以更改变量的值。
class B: A
* **Change Value when the class will initialize.**
override init()
super.init()
self.lightSaberColor = "Orange"
/*
* **Change Value when you will use this function.**
*/
func test()
lightSaberColor = "Green"
let b = B()
b.test()
【讨论】:
【参考方案12】:除了上面的例子,
class SomeClass
var hello = "hello"
class ChildClass: SomeClass
override var hello: String
set
_hello = newValue
get
_hello
private var _hello: String = ""
// Testing...
let c = ChildClass()
c.hello = "test"
print(c.hello) // test
【讨论】:
这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review【参考方案13】:我使用这种方法:
class Class
private lazy var _instance: AType =
return AType()
()
var instance: AType
return _instance
class Subclass: Class
private lazy var _instance: AType =
return ASubType()
()
override var instance: AType
return _instance
【讨论】:
以上是关于覆盖 Swift 中的存储属性的主要内容,如果未能解决你的问题,请参考以下文章