web-dev-qa-db-fra.com

Remplacement d'une propriété stockée dans Swift

J'ai remarqué que le compilateur ne me laissera pas remplacer une propriété stockée par une autre valeur stockée (ce qui semble étrange):

class Jedi {
    var lightSaberColor = "Blue"
}


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

Cependant, je suis autorisé à le faire avec une propriété calculée:

class Jedi {
    let lightSaberColor = "Blue"
}


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

}

Pourquoi ne suis-je pas autorisé à lui donner une autre valeur?

Pourquoi remplacer avec une propriété stockée une abomination et le faire avec une casher calculée? À quoi pensaient-ils?

87
cfischer

Pourquoi ne suis-je pas autorisé à lui donner une autre valeur?

Vous êtes définitivement autorisé à attribuer une valeur différente à une propriété héritée. Vous pouvez le faire si vous initialisez la propriété dans un constructeur qui prend cette valeur initiale et transmettez une valeur différente de la classe dérivée:

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)

Remplacer une propriété, ce n’est pas la même chose que de lui donner une nouvelle valeur, c’est plus comme donner à une classe une propriété différente. En fait, c'est ce qui se produit lorsque vous substituez une propriété calculée: le code qui calcule la propriété dans la classe de base est replacement / par un code qui calcule le remplacement de cette propriété dans la classe dérivée.

[Est-il] possible de remplacer la propriété stockée réelle, c'est-à-dire lightSaberColor qui a un autre comportement?

Hormis les observateurs, les propriétés stockées n'ont pas de comportement, il n'y a donc rien à remplacer. Donner à la propriété une valeur différente est possible grâce au mécanisme décrit ci-dessus. Cela correspond exactement à ce que l'exemple de la question tente de réaliser, avec une syntaxe différente.

52
dasblinkenlight

Pour moi, votre exemple ne fonctionne pas dans Swift 3.0.1.

Entré dans la cour de récréation ce code:

class Jedi {
    let lightsaberColor = "Blue"
}

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

Lance une erreur lors de la compilation dans Xcode:

ne peut pas remplacer la propriété immuable 'let', 'lightsaberColor', avec le getter d'un 'var'

Non, vous ne pouvez pas changer le type d'une propriété stockée. Le principe de substitution de Liskov oblige à autoriser l'utilisation d'une sous-classe dans un endroit où la super-classe est souhaitée.

Mais si vous le changez en var et ajoutez donc set dans la propriété calculée, vous pouvez remplacer la propriété stockée par une propriété calculée du même type.

class Jedi {
    var lightsaberColor = "Blue"
}


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

Cela est possible car il peut être judicieux de passer d'une propriété stockée à une propriété calculée.

Mais remplacer une propriété var stockée par une propriété var stockée n’a aucun sens, car vous ne pouvez changer que la valeur de la propriété.

Cependant, vous ne pouvez pas remplacer une propriété stockée par une propriété stockée.


Je ne dirais pas que les Sith sont Jedi :-P. Par conséquent, il est clair que cela ne peut pas fonctionner.

36
Binarian

Vous voulez probablement attribuer une autre valeur à la propriété:

class Jedi {
    var lightSaberColor = "Blue"
}


class Sith: Jedi {
    override init() {
        super.init()
        self.lightSaberColor = "Red"
    }
}
13
zisoft
class SomeClass {
    var hello = "hello"
}
class ChildClass: SomeClass {
    override var hello: String {
        set {
            super.hello = newValue
        }
        get {
            return super.hello
        }    
    }
}
9
Zhiping Yang

Pour Swift 4, à partir de documentation Apple :

Vous pouvez remplacer une instance héritée ou une propriété de type pour fournir votre propre getter et setter personnalisé pour cette propriété, ou pour ajouter observateurs de propriété pour permettre à la propriété dominante d’observer quand la valeur de la propriété sous-jacente change.

9
riyasavla

Dans Swift, ce n'est malheureusement pas possible. La meilleure alternative est la suivante:

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


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

Vous pouvez également utiliser une fonction pour remplacer. Ce n'est pas une réponse directe, mais peut enrichir ce sujet)

Classe A 

override func viewDidLoad() {
    super.viewDidLoad()

    if shouldDoSmth() {
       // do
    }
}

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

Classe B: A

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

J'ai eu le même problème pour définir une constante pour un contrôleur de vue.

Comme j'utilise un générateur d'interface pour gérer la vue, je ne peux pas utiliser init(); ma solution de contournement était donc similaire à d'autres réponses, à la différence que j'utilisais une variable calculée en lecture seule dans les classes de base et héritées.

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

class Sith: Jedi {
    override var type: String {
        get { return "Red" }
    }
}
0
Yassine El Badaoui