覆盖Swift中的存储属性

cfi*_*her 110 inheritance overriding properties swift

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

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override var lightSaberColor = "Red" // Cannot override with a stored property lightSaberColor
}
Run Code Online (Sandbox Code Playgroud)

但是,我允许使用计算属性执行此操作:

class Jedi {
    let lightSaberColor = "Blue"
}


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

}
Run Code Online (Sandbox Code Playgroud)

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

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

das*_*ght 65

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

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

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)
Run Code Online (Sandbox Code Playgroud)

覆盖属性与给它一个新值不同 - 它更像是给一个类一个不同的属性.实际上,当您覆盖计算属性时会发生这种情况:计算基类中的属性的代码被替换为计算派生类中该属性的覆盖的代码.

[是否]可以覆盖实际存储的属性,即lightSaberColor具有其他一些行为?

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

  • @MaxMacLeod除了观察者之外,存储的属性没有行为,因此实际上没有什么可以覆盖.他想在子类中为存储的属性赋予不同的值,但他不确定实现它的机制.答案解释了如何在Swift中完成.对于迟到的回复我很抱歉,看来您的评论引起了足够的混淆以吸引downvotes,所以我决定解释发生了什么. (2认同)

Bin*_*ian 48

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

进入游乐场这段代码:

class Jedi {
    let lightsaberColor = "Blue"
}

class Sith: Jedi {
    override var lightsaberColor : String {
        return "Red"
    }
}
Run Code Online (Sandbox Code Playgroud)

在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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为从存储属性切换到计算属性是有意义的.

但是使用存储var属性覆盖存储的var属性没有意义,因为您可以更改属性的值.

但是,您可以根本不使用存储属性覆盖存储的属性.


我不会说西斯是绝地:-P.因此很明显,这不起作用.


zis*_*oft 14

您可能想要为该属性分配另一个值:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 同样适用于上述评论 (8认同)

小智 14

class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然此代码段可以解决问题,但[包括解释](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers)确实有助于提高帖子的质量.请记住,您将来会回答读者的问题,而这些人可能不知道您的代码建议的原因. (8认同)

小智 9

对于Swift 4,来自Apple的文档:

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


Hug*_*nso 8

如果您尝试在 Swift 5 中这样做,您将收到一个

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

最好的办法是将其声明为计算属性。

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

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

class Red: Base {
   override var lightSaberColor: String { "red" }
}
Run Code Online (Sandbox Code Playgroud)


Noa*_*der 5

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

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


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


yoA*_*ex5 5

Swift does not allow you to override a variable stored property Instead of this you can use 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
}
Run Code Online (Sandbox Code Playgroud)
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())
}
Run Code Online (Sandbox Code Playgroud)

It is more clear when compare with Java, where a class field can not be overrode and does not support polymorphism because is defined in compile time(run efficiently). It is called a variable hiding[About] It is not recommended to use this technics because it is hard to read/support

[Swift property]