web-dev-qa-db-fra.com

Substitution de la super-classe avec un type différent dans Swift

Dans Swift, quelqu'un peut-il expliquer comment remplacer une propriété d'une superclasse par un autre objet sous-classé à partir de la propriété d'origine?

Prenons cet exemple simple:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override let chassis = RacingChassis() //Error here
}

Cela donne l'erreur:

Cannot override with a stored property 'chassis'

Si j'ai un châssis en 'var' à la place, j'obtiens l'erreur:

Cannot override mutable property 'chassis' of type 'Chassis' with covariant type 'RacingChassis'

La seule chose que je puisse trouver dans le guide sous "Propriétés de substitution" indique que nous devons remplacer le getter et le setter, ce qui peut fonctionner pour changer la valeur de la propriété (s'il s'agit de "var") ?

106
James

Swift ne vous permet pas de changer le type de classe d'aucune variable ou propriété. À la place, vous pouvez créer une variable supplémentaire dans la sous-classe qui gère le nouveau type de classe:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    var chassis = Chassis()
}
class RaceCar: Car {
    var racingChassis = RacingChassis()
    override var chassis: Chassis {
        get {
            return racingChassis
        }
        set {
            if newValue is RacingChassis {
                racingChassis = newValue as RacingChassis
            } else {
                println("incorrect chassis type for racecar")
            }
        }
    }
}

Il semble qu’on ne puisse pas déclarer une propriété avec la syntaxe let et la remplacer par var dans sa sous-classe ou vice-versa, ce qui peut être dû au fait que la mise en oeuvre de la superclasse pourrait ne pas s'attendre à ce que cette propriété change une fois initialisée. Ainsi, dans ce cas, la propriété doit également être déclarée avec "var" dans la super-classe pour correspondre à la sous-classe (comme indiqué dans l'extrait de code ci-dessus). Si vous ne pouvez pas modifier le code source dans la super-classe, il est probablement préférable de détruire le RaceCar actuel et de créer un nouveau RaceCar chaque fois que le châssis doit être muté.

101
Dash

Cela semble fonctionner

class Chassis {
    func description() -> String {
        return "Chassis"
    }
}
class RacingChassis : Chassis {
    override func description() -> String {
        return "Racing Chassis"
    }

    func racingChassisMethod() -> String {
        return "Wrooom"
    }
}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override var chassis: RacingChassis {
    get {
        return self.chassis
    }
    set {
        self.chassis = newValue
    }
    }
}

var car = Car()
car.chassis.description()

var raceCar = RaceCar()
raceCar.chassis.description()
raceCar.chassis.racingChassisMethod()
9
David Arve

Essaye ça:

class Chassis{
     var chassis{
         return "chassis"
     } 
}

class RacingChassis:Chassis{
     var racing{
         return "racing"
     } 
}

class Car<Type:Chassis> {
     let chassis: Type
     init(chassis:Type){
        self.chassis = chassis
     }
}

class RaceCar: Car<RacingChassis> {
     var description{
         return self.chassis.racing
     } 
}

Ensuite:

let racingChassis = RacingChassis()
let raceCar = RaceCar(chassis:racingChassis)
print(raceCar.description) //output:racing

Détail dans http://www.mylonly.com/14957025459875.html

5
adimtxg0422

Le tableau de bord Solution fourni fonctionne bien, sauf que la super-classe doit être déclarée avec le mot-clé let plutôt qu'avec var. Voici une solution qui est possible mais non recommandée!

La solution ci-dessous compilera avec Xcode 6.2, Swift 1.1 (si toutes les classes sont dans des fichiers Swift différents), mais doit être évitée car elle peut conduire à des comportements inattendus (y compris un crash, en particulier lors de l'utilisation de types non facultatifs) . NOTE: CECI NE FONCTIONNE PAS AVEC XCODE 6.3 BETA 3, Swift 1.2.

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    var chassis:Chassis? = Chassis()
}

class RaceCar: Car {
    override var chassis: RacingChassis? {
        get {
            return super.chassis as? RacingChassis
        }
        set {
            super.chassis = newValue
        }
    }
}
3
Lukman

Théoriquement, vous êtes autorisé à le faire de cette façon ...

class ViewController {

    var view: UIView! { return _view }

    private var _view: UIView!
}

class ScrollView : UIView {}

class ScrollViewController : ViewController {

    override var view: ScrollView! { return super.view as ScrollView! }
}

class HomeView : ScrollView {}

class HomeViewController : ScrollViewController {

    override var view: HomeView! { return super.view as HomeView! }
}

Cela fonctionne parfaitement dans un terrain de jeu Xcode.

Mais , si vous essayez ceci dans un projet réel, une erreur du compilateur vous dit:

La déclaration 'view' ne peut pas écraser plus d'une déclaration de superclasse

J'ai seulement vérifié Xcode 6.0 GM pour l'instant.

Malheureusement, vous devrez attendre que Apple corrige cela.

J'ai aussi soumis un rapport de bogue. 18518795

1
aleclarson

J'ai vu de nombreuses raisons pour lesquelles la conception d'une API utilisant des variables plutôt que des fonctions est problématique et pour moi, utiliser des propriétés calculées ressemble à une solution de contournement. Il y a de bonnes raisons de garder vos variables d'instance encapsulées. Ici, j'ai créé un protocole Automobile auquel la voiture se conforme. Ce protocole a une méthode d'accès qui renvoie un objet Chassis. Comme Car s'y conforme, la sous-classe RaceCar peut la remplacer et renvoyer une sous-classe de châssis différente. Cela permet à la classe Car de programmer une interface (Automobile) et la classe RaceCar connaissant RacingChassis peut accéder directement à la variable _racingChassis.

class Chassis {}
class RacingChassis: Chassis {}

protocol Automobile {
    func chassis() -> Chassis
}

class Car: Automobile {
    private var _chassis: Chassis

    init () {
        _chassis = Chassis()
    }

    func chassis() -> Chassis {
        return _chassis
    }
}

class RaceCar: Car {
    private var _racingChassis: RacingChassis

    override init () {
        _racingChassis = RacingChassis()
        super.init()
    }

    override func chassis() -> Chassis {
        return _racingChassis
    }
}

Un autre exemple de la raison pour laquelle la conception d'une API utilisant des variables est défaillante est lorsque vous avez des variables dans un protocole. Si vous souhaitez décomposer toutes les fonctions de protocole en extensions, les propriétés stockées ne peuvent pas être placées dans des extensions et doivent être définies dans la classe AdaptableViewController et supprimez la variable mode de l’extension):

protocol Adaptable {
    var mode: Int { get set }
    func adapt()
}

class AdaptableViewController: UIViewController {
    // var mode = 0
}

extension AdaptableViewController: Adaptable {

    var mode = 0 // compiler error

    func adapt() {
        //TODO: add adapt code
    }
}

Le code ci-dessus comportera cette erreur de compilation: "Les propriétés des extensions ne sont peut-être pas stockées". Voici comment vous pouvez réécrire l'exemple ci-dessus afin que tout dans le protocole puisse être séparé dans l'extension en utilisant des fonctions à la place:

protocol Adaptable {
    func mode() -> Int
    func adapt()
}

class AdaptableViewController: UIViewController {
}

extension AdaptableViewController: Adaptable {
    func mode() -> Int {
        return 0
    }
    func adapt() {
        // adapt code
    }
}
1
Korey Hinton

Selon la manière dont vous envisagez d'utiliser la propriété, la méthode la plus simple consiste à utiliser un type facultatif pour votre sous-classe et à remplacer la méthode didSet {} pour le super:

class Chassis { }
class RacingChassis: Chassis { }

class Car {
    // Declare this an optional type, and do your 
    // due diligence to check that it's initialized
    // where applicable
    var chassis: Chassis?
}
class RaceCar: Car {
    // The subclass is naturally an optional too
    var racingChassis: RacingChassis?
    override var chassis: Chassis {
        didSet {
            // using an optional, we try to set the type
            racingChassis = chassis as? RacingChassis
        }
    }
}

Évidemment, vous devez passer du temps à vérifier que les classes peuvent être initialisées de cette façon, mais en définissant les propriétés sur facultatif, vous vous protégez contre les situations dans lesquelles le transtypage ne fonctionne plus.

0
brandonscript

Essaye ça:

class Chassis {}
class RacingChassis : Chassis {}
class SuperChassis : RacingChassis {}

class Car {
    private var chassis: Chassis? = nil
    func getChassis() -> Chassis? {
        return chassis
    }

    func setChassis(chassis: Chassis) {
        self.chassis = chassis
    }
}

class RaceCar: Car {
    private var chassis: RacingChassis {
        get {
            return getChassis() as! RacingChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = RacingChassis()
    }
}

class SuperCar: RaceCar {
    private var chassis: SuperChassis {
        get {
            return getChassis() as! SuperChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = SuperChassis()
    }
}
0
Fried Rice

Ce qui suit permet d'utiliser un seul objet dans les classes de base et dérivées. Dans la classe dérivée, utilisez la propriété d'objet dérivé.

class Car {
    var chassis:Chassis?

    func inspect() {
        chassis?.checkForRust()
    }
}

class RaceCar: Car {
    var racingChassis: RacingChassis? {
        get {
            return chassis as? RacingChassis
        } 
    }

    override func inspect() {
        super.inspect()
        racingChassis?.tuneSuspension()
    }
}
0
Stickley

Vous pouvez y arriver avec l'utilisation de génériques:

class Descriptor {
    let var1 = "a"
}

class OtherDescriptor: Descriptor {
    let var2 = "b"
}

class Asset<D: Descriptor> {
    let descriptor: D

    init(withDescriptor descriptor: D) {
        self.descriptor = descriptor
    }

    func printInfo() {
        print(descriptor.var1)
    }
}

class OtherAsset<D: OtherDescriptor>: Asset<D> {
    override func printInfo() {
        print(descriptor.var1, descriptor.var2)
    }
}

let asset = Asset(withDescriptor: Descriptor())
asset.printInfo() // a

let otherAsset = OtherAsset(withDescriptor: OtherDescriptor())
otherAsset.printInfo() // a b

Avec cette approche, vous obtiendrez un code de type 100% sécurisé sans décompression forcée.

Mais que ce soit une sorte de bidouille, et si vous devez redéfinir plusieurs propriétés, les déclarations de classe ressembleront à un gâchis total. Alors soyez prudent avec cette approche.

0
Al Zonke

il suffit de définir une nouvelle propriété imageview avec une convention de nommage différente, comme imgview, car imageView est déjà sa propre propriété et nous ne pouvons pas affecter 2 propriétés fortes.

0
Ali Rafiq

Vous pouvez simplement créer une autre variable de RacingChassis.

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    let chassis: Chassis
    init(){
        chassis = Chassis()
}}

class RaceCar: Car {
let raceChassis: RacingChassis
init(){
        raceChassis = RacingChassis()
}}
0
Anton
class Chassis {}
class RacingChassis : Chassis {}

class Car {
    fileprivate let theChassis: Chassis
    var chassis: Chassis {
        get {
            return theChassis
        }
    }
    fileprivate init(_ chassis: Chassis) {
        theChassis = chassis
    }
    convenience init() {
        self.init(Chassis())
    }
}
class RaceCar: Car {
    override var chassis: RacingChassis {
        get {
            return theChassis as! RacingChassis
        }
    }
    init() {
        super.init(RacingChassis())
    }
}
0
György Benedek