Überschreiben einer gespeicherten Eigenschaft in Swift

126

Ich habe festgestellt, dass der Compiler nicht zulässt, dass ich eine gespeicherte Eigenschaft mit einem anderen gespeicherten Wert überschreibe (was seltsam erscheint):

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override var lightSaberColor = "Red" // Cannot override with a stored property lightSaberColor
}

Ich darf dies jedoch mit einer berechneten Eigenschaft tun:

class Jedi {
    let lightSaberColor = "Blue"
}


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

}

Warum darf ich ihm keinen anderen Wert geben?

Warum ist das Überschreiben mit einer gespeicherten Eigenschaft ein Greuel und das mit einem berechneten koscheren Objekt? Was denken sie?

cfischer
quelle

Antworten:

82

Warum darf ich ihm nicht einfach einen anderen Wert geben?

Sie dürfen einer geerbten Eigenschaft definitiv einen anderen Wert geben. Sie können dies tun, wenn Sie die Eigenschaft in einem Konstruktor initialisieren, der diesen Anfangswert annimmt, und einen anderen Wert als die abgeleitete Klasse übergeben:

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)

Das Überschreiben einer Eigenschaft ist nicht dasselbe wie das Geben eines neuen Werts - es ist eher so, als würde man einer Klasse eine andere Eigenschaft geben. Genau das passiert, wenn Sie eine berechnete Eigenschaft überschreiben: Der Code, der die Eigenschaft in der Basisklasse berechnet, wird durch Code ersetzt, der die Überschreibung für diese Eigenschaft in der abgeleiteten Klasse berechnet.

[Ist es] möglich, die tatsächlich gespeicherte Eigenschaft zu überschreiben, dh lightSaberColordas hat ein anderes Verhalten?

Abgesehen von Beobachtern haben gespeicherte Eigenschaften kein Verhalten, sodass wirklich nichts zu überschreiben ist. Durch den oben beschriebenen Mechanismus ist es möglich, der Eigenschaft einen anderen Wert zu geben. Dies macht genau das, was das Beispiel in der Frage mit einer anderen Syntax erreichen will.

dasblinkenlight
quelle
2
@MaxMacLeod Abgesehen von Beobachtern haben gespeicherte Eigenschaften kein Verhalten, sodass wirklich nichts zu überschreiben ist. Er wollte einer gespeicherten Eigenschaft einen anderen Wert in der Unterklasse geben, war sich jedoch nicht sicher, wie dies erreicht werden sollte. Die Antwort erklärt, wie es in Swift gemacht werden kann. Es tut mir leid für die späte Antwort. Es scheint, dass Ihr Kommentar genug Verwirrung stiftet, um negative Stimmen anzulocken. Deshalb habe ich beschlossen, zu erklären, was los ist.
Dasblinkenlight
55

Für mich funktioniert Ihr Beispiel in Swift 3.0.1 nicht.

Ich habe auf dem Spielplatz diesen Code eingegeben:

class Jedi {
    let lightsaberColor = "Blue"
}

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

Wirft beim Kompilieren einen Fehler in Xcode:

kann unveränderliche 'let'-Eigenschaft' Lichtschwertfarbe 'nicht mit dem Getter eines' var 'überschreiben.

Nein, Sie können den Typ der gespeicherten Eigenschaft nicht ändern. Das Liskov-Substitutionsprinzip zwingt Sie dazu, zuzulassen, dass eine Unterklasse an einem Ort verwendet wird, an dem die Oberklasse gewünscht wird.

Wenn Sie es jedoch in die berechnete Eigenschaft ändern varund daher hinzufügen set, können Sie die gespeicherte Eigenschaft mit einer berechneten Eigenschaft desselben Typs überschreiben.

class Jedi {
    var lightsaberColor = "Blue"
}


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
        set {
            // nothing, because only red is allowed
        }
    }
}

Dies ist möglich, weil es sinnvoll sein kann, von einer gespeicherten Eigenschaft zu einer berechneten Eigenschaft zu wechseln.

Das Überschreiben einer gespeicherten varEigenschaft mit einer gespeicherten varEigenschaft ist jedoch nicht sinnvoll, da Sie nur den Wert der Eigenschaft ändern können.

Sie können eine gespeicherte Eigenschaft jedoch überhaupt nicht mit einer gespeicherten Eigenschaft überschreiben.


Ich würde nicht sagen, dass Sith Jedi sind :-P. Daher ist es klar, dass dies nicht funktionieren kann.

Binarian
quelle
18
class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}
Zhiping Yang
quelle
13
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage in Zukunft für Leser beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
DimaSan
15

Sie möchten der Eigenschaft wahrscheinlich einen anderen Wert zuweisen:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}
zisoft
quelle
9
Gleiches gilt wie oben angegeben
Max MacLeod
10

Für Swift 4 aus Apples Dokumentation :

Sie können eine geerbte Instanz oder Typ-Eigenschaft überschreiben, um Ihren eigenen benutzerdefinierten Getter und Setter für diese Eigenschaft bereitzustellen, oder Eigenschaftsbeobachter hinzufügen, damit die überschreibende Eigenschaft beobachten kann, wenn sich der zugrunde liegende Eigenschaftswert ändert.

Riyasavla
quelle
7

In Swift ist dies leider nicht möglich. Die beste Alternative ist die folgende:

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


class Sith: Jedi {
    override var lightsaberColor : String {
        get {
            return "Red"
        }
    }
}
Noah Wilder
quelle
3

Ich hatte das gleiche Problem, eine Konstante für einen View Controller festzulegen.

Da ich zum Verwalten der Ansicht den Interface Builder verwende, kann ich ihn nicht verwenden init(). Daher war meine Problemumgehung ähnlich wie bei anderen Antworten, außer dass ich eine schreibgeschützte berechnete Variable sowohl für Basisklassen als auch für geerbte Klassen verwendet habe.

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

class Sith: Jedi {
    override var type: String {
        get { return "Red" }
    }
}
Yassine ElBadaoui
quelle
3

Wenn Sie dies in Swift 5 versuchen, werden Sie von a begrüßt

Die unveränderliche 'let'-Eigenschaft' lightSaberColor 'kann nicht mit dem Getter eines' var 'überschrieben werden.

Am besten deklarieren Sie es als berechnete Eigenschaft.

Dies funktioniert, da wir nur die get {}Funktion überschreiben

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

class Red: Base {
   override var lightSaberColor: String { "red" }
}
Hugo Alonso
quelle
2

Mit Swift können Sie eine Variable nicht überschreiben. stored propertyStattdessen können Sie sie verwendencomputed 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())
}

Im Vergleich zu Java ist es klarer, wenn ein Klassenfeld nicht überschrieben werden kann und keinen Polymorphismus unterstützt, da es in der Kompilierungszeit definiert ist (effizient ausgeführt wird). Es wird eine Variable genannt, die [About] versteckt. Es wird nicht empfohlen, diese Technik zu verwenden, da sie schwer zu lesen / zu unterstützen ist

[Schnelle Eigenschaft]

yoAlex5
quelle
1

Sie können auch eine Funktion zum Überschreiben verwenden. Es ist keine direkte Antwort, kann aber dieses Thema bereichern.

Klasse a

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

Klasse B: A.

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