2016-09-27 2 views
0

Ce que j'ai essayé de faire:Comment remplacer les méthodes qui ont des paramètres de type associés dans les sous-classes?

protocol HasElement { 
    associatedtype ItemType 
    func getElement() -> ItemType 
    func setElement(element: ItemType) 
} 

class Element {} 
class BarElement: Element {} 

class Foo: NSObject, HasElement { 
    typealias ItemType = Element 
    func getElement() -> Element { ... } 
    func setElement(element: Element) { ... } 
} 

class Bar: Foo { 
    typealias ItemType = BarElement 
    override func getElement() -> BarElement { ... } // This works. 
    override func setElement(element: BarElement) { ... } // This fails. 
} 

L'erreur est:

méthode ne l'emportait pas sur toute méthode de sa superclasse

Si je tente de ItemType utiliser à la place:

L'erreur est:

« ItemType » est ambigu pour le type recherche dans ce contexte

est-il un moyen de faire ce travail?

+0

Avez-vous essayé de le définir en tant qu'élément? Devrait être moins "ambigu" –

+0

Ensuite, je devrais le mouler pour l'utiliser dans les sous-classes. Tout le but de cet exercice est de ne pas avoir à lancer la chose sacrée tout le temps. –

+0

Mais oui, ça marche. –

Répondre

1

est ici un moyen de faire ce que vous voulez:

protocol HasElement { 
    associatedtype ItemType 
    func getElement() -> ItemType 
    func setElement(element: ItemType) 
} 

class Element {} 
class BarElement: Element {} 

class Foo: HasElement { 
    // no need for typealias, the associated type is inferred 
    func getElement() -> Element { return Element() } 
    func setElement(element: Element) { } 
} 

class Bar: Foo { 
    // no need for typealias, the associated type is inferred 
    override func getElement() -> BarElement { return BarElement() } 

    // hide the parent class method 
    @available(*, unavailable, message: "Use setElement(element: BarElement)") 
    override func setElement(element: Element) { } 

    // comply with protocol in this class 
    func setElement(element: BarElement) { } 
} 

// can't do this now: 
let myElement = Element() 
let myBar = Bar() 
myBar.setElement(element: myElement) // Error: 'setElement(element: BarElement)' is unavailable: Use setElement(element: BarElement) 
+1

Solution intéressante, mais notez qu'en surchargeant au lieu de surcharger vous perdez la répartition dynamique, ce qui peut être souhaitable ou non. Si vous définissez 'myBar' comme' let myBar: Foo = Bar() ', alors je m'attendrais à ce que la méthode' setElement (élément:) 'de' Foo' soit appelée, plutôt que la méthode 'Bar'. Bien que ce qui est intéressant est que, en faisant cela, aucune méthode ne semble être appelée - jusqu'à ce que vous supprimiez l'attribut indisponible. – Hamish

+1

Je suis d'accord que ce n'est peut-être pas la meilleure solution. Je pense que l'héritage n'est peut-être pas la solution, mais une approche basée sur le protocole pourrait donner de meilleurs résultats. – ColGraff

1

Le problème ici ne sont pas types associés, il est que les entrées méthode sont contravariant. Par conséquent, vous ne pouvez pas remplacer une méthode qui attend une entrée d'instance de superclasse donnée avec une méthode qui attend une entrée d'instance de sous-classe.

En fait, vous pouvez simplement faire bouillir votre code jusqu'à:

class Element {} 
class BarElement : Element {} 

class Foo { 
    func setElement(element: Element) { } 
} 

class Bar : Foo { 
    // error: Method does not override any method from its superclass 
    override func setElement(element: BarElement) { } 
} 

Vous ne pouvez pas passer outre une méthode (Element) -> Void avec une méthode (BarElement) -> Void. Les raisons de ceci devraient être assez évidentes si vous considérez ce qui se produirait si vous créiez une instance Bar et que vous l'affiniez à Foo. Vous êtes maintenant en mesure de passer une instance Element à une méthode qui attend une instance BarElement, ce qui est illégal.

La raison pour laquelle cela fonctionne pour votre méthode getElement est que les sorties de méthode sont covariant. Par conséquent, le remplacement d'une méthode () -> Element par une méthode () -> BarElement est parfaitement légal, car même si vous transférez une instance Bar à Foo, l'instance BarElement renvoyée par getElement peut être librement convertie en Element. En ce qui concerne les solutions, cela dépend plutôt de votre cas d'utilisation exact. Il se peut bien que ce que vous essayez de faire ne nécessite pas d'héritage du tout, et que vous puissiez simplement vous conformer Foo et Bar séparément à HasElement.

+0

Ouais, c'est juste. –