覆盖 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 中的存储属性的主要内容,如果未能解决你的问题,请参考以下文章

覆盖 Swift 扩展中的方法

Swift 中的属性

从0开始学Swift笔记整理

22.swift属性

《从零开始学Swift》学习笔记(Day 31)——存储属性

Core Data 和 Swift 中的计算属性与持久存储