2016-09-05 2 views
3

est-il possible de prendre en charge les types de retour covariant dans Swift?Swift: les substitutions covariantes?

Par exemple, je voudrais soutenir le scénario suivant:

class Animal {} 
class Dog : Animal{} 
class Cat : Animal {} 

class AnimalResidency{ 
    func getAllAnimals() -> Array<Animal> { 
     return [] 
    } 
} 
class Cattery : AnimalResidency{ 
    override func getAllAnimals() -> Array<Cat> { 
     return [Cat(), Cat()] 
    } 
} 
class DogKennel : AnimalResidency { 
    override func getAllAnimals() -> Array<Dog> { 
     return [Dog(), Dog(), Dog(), Dog()] 
    } 
} 

Func surchargée produit une erreur de compilation parce que la signature de overrides ne correspond pas exactement à la définition de base, même si il est clair pour voir le contenu renvoyé par le remplacement correspond toujours au contrat de la définition de base.

Y at-il un moyen pour moi d'atteindre ce qui précède? J'apprécierais même une réponse pour Swift 3.

Répondre

4

Je ne suis pas sûr qu'il est nécessaire de faire exactement ce que vous demandez - à savoir overridegetAllAnimals plutôt que de le surcharger. Génériques est une en utilisant des solutions possibles - voir si cela fonctionne pour vous:

class Animal { var description: String { return "Animal" } } 
class Dog : Animal { override var description: String { return "Dog" } }   
class Cat : Animal { override var description: String { return "Cat" } } 

class AnimalResidency<T: Animal>{ 
    func getAllAnimals<T>() -> Array<T> { 
     return [] 
    } 
} 

class Cattery : AnimalResidency<Cat> { 
    func getAllAnimals() -> Array<Cat> { 
     return [Cat()] 
    } 
} 

class DogKennel : AnimalResidency<Dog> { 
    func getAllAnimals() -> Array<Dog> { 
     return [Dog(), Dog()] 
    } 
} 

let c = Cattery() 
c.getAllAnimals().first?.description // "Cat" 
let d = DogKennel() 
d.getAllAnimals().first?.description // "Dog" 

Ma pensée ne serait pas utilisé deux hiérarchies de classes parallèles, cependant, mais d'essayer quelque chose comme ça ...

class Animal { 
    var description: String { return "Animal" } 
    required init() {} 
} 
class Dog : Animal { 
    override var description: String { return "Dog" } 
} 
class Cat : Animal { 
    override var description: String { return "Cat" } 
} 

extension Animal { 
    class func home() -> [Animal] { 
     return [self.init()] 
    } 
} 

let c = Cat.home().first?.description // "Cat" 
let d = Dog.home().first?.description // "Dog" 
0

Comme vous l'avez déjà remarqué, les signatures doivent être exactement les mêmes. Mais vous pouvez utiliser [Base] comme type de retour dans votre classe Derived.

class Base { 
    var a = "A" //just a property to check if you can access properties of this class 

    var test: [Base] { 
     return [self] 
    } 

    func test2() -> [Base] { 
     return [self] 
    } 
} 

class Derived: Base { 
    var b = "B" //just a property to check if you can access properties of this class 

    override var test: [Base] { 
     return [self] 
    } 

    override func test2() -> [Base] { 
     return [self] 
    } 
} 

Vous pouvez soit utiliser test2() ou test (une propriété calculée).

Avant de pouvoir accéder à une propriété d'un objet stocké dans le tableau, vous devez jeter commeDerived.

let d = Derived() 
let dArray = d.test2() 

print((dArray[0] as! Derived).b) //Prints "B" 
+0

Cette approche se décompose sur la ligne d'impression où vous devez forcer le cast à 'Derived' pour accéder à' b'. OP aimerait que les classes dérivées renvoient des types spécifiques dérivés du type de retour de super, de sorte que la conversion n'est pas nécessaire. –

1

Un paradigme orienté protocole d'utilisation rapide. Donc, basé sur votre demande. Il est préférable d'utiliser des protocoles et des PAT dans votre cas.

What are PATs.

protocol BaseProtocol { 
    //PAT's 
    typealias ReturnType: BaseType 
    func someFunction() -> ReturnType 
} 

class SomeClass : BaseProtocol { 
    func someFunction() -> BaseType { } 
} 
+1

Désolé, je ne vois aucune preuve de covariance dans l'exemple que vous avez posté. Pourriez-vous indiquer où la covariance s'applique s'il vous plaît? –

0

Comme mentionné dans FelixSFD de répondre à vos conflits Vise avec paradigme orienté interface. HeadOffice devrait utiliser l'interface de Office, ex:

let staff: [Office] = [Office(), HeadOffice()] 
for person in staff { 
    // every `person` responds to `Office` interface 
} 

Dans votre cas, je propose d'utiliser les médicaments génériques parce qu'ils sont conçus pour ce genre de choses:

func getAllStaff<T>(obj: T) -> Array<T?> { 
    return [obj] 
} 

Vous pouvez également mettre en œuvre getAllStaff comme mode de classe, en fonction de vos besoins ...