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

}

別の値を指定できないのはなぜですか?

格納されたプロパティでオーバーライドするのが嫌悪で、計算されたコーシャでそれを行うのはなぜですか?彼らはどこを考えていますか?


回答:


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でコンパイル時にエラーをスローします。

不変の「let」プロパティ「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。したがって、これが機能しないことは明らかです。


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

13
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
DimaSan 2017年

15

あなたはおそらく別の値をプロパティに割り当てたいでしょう:

class Jedi {
    var lightSaberColor = "Blue"
}


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

9
上記のコメントと同じ
Max MacLeod

10

Swift 4の場合、Appleのドキュメントから:

継承されたインスタンスまたはタイププロパティをオーバーライドして、そのプロパティに独自のカスタムゲッターとセッターを提供したり、プロパティオブザーバーを追加して、基になるプロパティ値が変化したときにオーバーライドするプロパティを監視したりできます。


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でそれを行おうとすると、

不変の「let」プロパティ「lightSaberColor」を「var」のゲッターでオーバーライドすることはできません

あなたの最善の策は、それを計算されたプロパティとして宣言することです。

これは、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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.