覆盖Swift中的存储属性


126

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

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"}

}

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

为什么用存储的属性覆盖是可憎的,而用计算的一个犹太洁食呢?他们在想什么呢?


Answers:


82

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

绝对可以为继承的属性赋予不同的值。如果您在采用该初始值的构造函数中初始化属性,然后传递与派生类不同的值,则可以执行以下操作:

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具有其他行为?

除了观察者之外,存储的属性没有行为,因此实际上没有任何东西可以覆盖。通过上述机制可以为属性提供不同的值。这确实使用不同的语法来完成问题中的示例所要实现的目标。


2
@MaxMacLeod除了观察者之外,存储的属性没有行为,因此实际上没有要覆盖的内容。他想给子类一个存储的属性一个不同的值,但是他不确定实现它的机制。答案解释了如何在Swift中完成它。抱歉,您的评论很晚,看来您的评论引起了足够的混乱,无法吸引到投票者,所以我决定解释一下情况。
dasblinkenlight

55

对我来说,您的示例在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。因此很明显,这是行不通的。


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

13
尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
DimaSan

15

您可能想为属性设置另一个值:

class Jedi {
    var lightSaberColor = "Blue"
}


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

9
同样适用于上述评论
Max MacLeod

10

对于Swift 4,Apple的文档中

您可以覆盖继承的实例或类型属性,以为该属性提供自己的自定义getter和setter,或添加属性观察器以使覆盖的属性能够在基础属性值更改时进行观察。


7

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

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


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

3

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

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

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

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

3

如果您尝试在Swift 5中执行此操作,则会被一个

无法使用'var'的getter覆盖不可变的'let'属性'lightSaberColor'

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

这是有效的,因为我们只是覆盖了get {}功能

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

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

2

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相比,它更加清楚,因为在编译时定义了类字段(有效运行),所以类字段不能被覆盖并且不支持多态。它称为变量隐藏[关于]不建议使用此技术,因为它很难阅读/支持

[迅捷属性]


1

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

A级

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

B级:A

public func shouldDoSmth() -> Bool {
    return false
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.