覆盖Swift中的存储属性

时间:2014-11-01 18:56:00

标签: inheritance swift properties override

我注意到编译器不允许我用另一个存储值覆盖存储的属性(这似乎很奇怪):

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 : String{return "Red"}

}

为什么我不允许给它另一个值?

为什么用存储的属性覆盖令人憎恶的东西,并用计算的一个犹太教来做?他们在想什么?

11 个答案:

答案 0 :(得分:54)

  

为什么我不允许再给它一个值?

绝对允许您为继承的属性提供不同的值。如果在获取该初始值的构造函数中初始化该属性,并从派生类传递不同的值,则可以执行此操作:

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

除了观察者之外,存储的属性没有行为,因此实际上没有什么可以覆盖。通过上述机制可以赋予财产不同的价值。这正是问题中的示例试图用不同的语法实现的。

答案 1 :(得分:40)

对我来说,你的例子在Swift 3.0.1中不起作用。

在游乐场输入此代码:

class Jedi {
    let lightsaberColor = "Blue"
}

class Sith: Jedi {
    override var lightsaberColor : String {
        return "Red"
    }
}

在Xcode的编译时抛出错误:

  

不能覆盖不可变的'让' property' lightsaberColor'用一个' var'

的吸气剂

否则您无法更改存储属性的类型。 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。因此很明显,这不起作用。

答案 2 :(得分:13)

您可能想要为该属性指定另一个值:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}

答案 3 :(得分:10)

class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}

答案 4 :(得分:9)

对于Swift 4,来自Apple's documentation

  

您可以覆盖要提供的继承实例或类型属性   您自己的该属性的自定义getter和setter,或者要添加   属性观察者使覆盖属性能够观察何时   基础财产价值变动。

答案 5 :(得分:3)

不幸的是,这在Swift中是不可能做到的。最好的替代方法如下:

class Jedi {
    private(set) var lightsaberColor = "Blue"
}


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
    }
}

答案 6 :(得分:3)

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]

答案 7 :(得分:1)

我在为视图控制器设置常量时遇到了同样的问题。

由于我使用界面生成器来管理视图,因此无法使用init(),因此我的解决方法与其他答案类似,不同之处在于,我在基类和继承类上均使用了只读计算变量。 / p>

class Jedi {
    var type: String {
        get { return "Blue" }
    }
}

class Sith: Jedi {
    override var type: String {
        get { return "Red" }
    }
}

答案 8 :(得分:0)

您还可以使用函数来覆盖。这不是直接的答案,但是可以丰富这个主题)

A级

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

public func shouldDoSmth() -> Bool {
    return true
}

B级:A

public func shouldDoSmth() -> Bool {
    return false
}

答案 9 :(得分:0)

如果您尝试在Swift 5中执行此操作,则会受到

的欢迎。
  

不能用'var'的获取者覆盖不可变的'let'属性'lightSaberColor'

您最好的选择是将其声明为计算属性。

这是有效的,因为我们只是覆盖get {}函数

class Base {
   var lightSaberColor: String { "base" }
}

class Red: Base {
   override var lightSaberColor: String { "red" }
}

答案 10 :(得分:-1)

您可以使用此方法更新值

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()