web-dev-qa-db-fra.com

L'utilisation d'un protocole en tant que type concret conforme à un autre protocole n'est pas prise en charge

J'essaie de mélanger des génériques avec des protocoles et j'ai vraiment beaucoup de mal à xD

Certaines architectures sont implémentées dans un projet Android/Java et j’essaie de la réécrire pour l’adapter à un projet Swift/iOS. Mais j’ai trouvé cette limitation.

ProtocolA

protocol ProtocolA {

}

ProtocolB

protocol ProtocolB : ProtocolA {

}

ImplementProtocolA

class ImplementProtocolA <P : ProtocolA> {

    let currentProtocol : P

    init(currentProtocol : P) {
        self.currentProtocol = currentProtocol
    }

}

ImplementProtocolB

class ImplementProtocolB : ImplementProtocolA<ProtocolB> {

}

Donc, lorsque je tente de définir ProtocolB comme type concret qui implémente ProtocolA , Je reçois cette erreur:

L'utilisation de 'ProtocolB' comme type concret conforme au protocole 'ProtocolA' n'est pas prise en charge

1 Y a-t-il une raison pour cette "limitation"?

2 Existe-t-il une solution de contournement pour mettre cela en œuvre?

3 Sera-t-il pris en charge à un moment donné?

- MISE À JOUR -

Une autre variante du même problème, je pense:

Voir les protocoles

protocol View {

}

protocol GetUserView : View {
    func showProgress()
    func hideProgress()
    func showError(message:String)
    func showUser(userDemo:UserDemo)
}

Protocoles du présentateur

protocol Presenter {
    typealias V : View
}

class UserDemoPresenter : Presenter {
    typealias V = GetUserView
}

Erreur:

UserDemoPresenter.Swift La correspondance éventuellement prévue "V" (ou "GetUserView") n'est pas conforme à "Affichage"

Qu'est-ce que c'est?? C'est conforme!

Même si j'utilise View au lieu de GetUserView, cela ne compile pas.

class UserDemoPresenter : Presenter {
    typealias V = View
}

UserDemoPresenter.Swift La correspondance éventuellement prévue 'V' (ou 'View') n'est pas conforme à 'View'

xxDD Je ne comprends pas, vraiment.

- MISE À JOUR -

Avec la solution proposée par Rob Napier, le problème n'est pas résolu, il est simplement différé.

Lorsque j'essaie de définir une référence à UserDemoPresenter, je dois spécifier le type générique afin d'obtenir la même erreur:

private var presenter : UserDemoPresenter<GetUserView>

L'utilisation de 'GetUserView' en tant que type concret conforme au protocole 'GetUserView' n'est pas prise en charge

61
Víctor Albertos

La raison sous-jacente de la limitation est que Swift n’a pas de métatypes de première classe. L’exemple le plus simple est que cela ne fonctionne pas:

func isEmpty(xs: Array) -> Bool {
    return xs.count == 0
}

En théorie, ce code pourrait fonctionner, et s'il le faisait, je pourrais en créer beaucoup d'autres (comme Functor et Monad, qui ne peuvent vraiment pas être exprimés en Swift aujourd'hui). Mais vous ne pouvez pas. Vous devez aider Swift clouer ceci au type concret. Nous le faisons souvent avec des génériques:

func isEmpty<T>(xs: [T]) -> Bool {
    return xs.count == 0
}

Notez que T est totalement redondant ici. Il n'y a aucune raison que je devrais avoir à l'exprimer; ce n'est jamais utilisé. Mais Swift le requiert pour pouvoir transformer l'abstraction Array en béton [T]. La même chose est vraie dans votre cas.

C'est un type concret (enfin, c'est un type abstrait qui sera transformé en un type concret chaque fois qu'il sera instancié et que P sera renseigné):

class ImplementProtocolA<P : ProtocolA>

C'est un type totalement abstrait qui Swift n'a aucune règle à transformer en un type concret:

class ImplementProtocolB : ImplementProtocolA<ProtocolB>

Vous devez le rendre concret. Cela va compiler:

class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}

Et aussi:

class UserDemoPresenter<T: GetUserView> : Presenter {
    typealias V = T
}

Tout simplement parce que vous rencontrerez probablement ce problème plus tard: votre vie sera bien plus facile si vous créez ces classes ou ces classes final. Les protocoles de mélange, les génériques et le polymorphisme de classe sont pleins d'arêtes très vives. Parfois, vous avez de la chance et ça ne compile pas. Parfois, il appellera des choses inattendues.

Vous pouvez être intéressé par n peu de respect pour AnySequence qui détaille certaines questions connexes.


private var presenter : UserDemoPresenter<GetUserView>

C'est toujours un type abstrait. Vous voulez dire:

final class Something<T: GetUserView> {
    private var presenter: UserDemoPresenter<T>
}

Si cela crée un problème, vous devrez créer une boîte. Voir Le protocole ne se conforme pas à lui-même? pour en savoir plus sur la manière dont vous voulez effacer le texte pour pouvoir conserver les types abstraits. Mais vous devez travailler dans des types concrets. Vous ne pouvez pas finalement vous spécialiser sur un protocole. Vous devez éventuellement vous spécialiser sur quelque chose de concret dans la majorité des cas.

61
Rob Napier