[swift] Generics et spécialisation
laurris
Membre
Voilà donc une classe Vehicule que je souhaite spécialiser en Vehicule<Voiture>.
Vehicule peut être initialisée avec un type explicite. Ici Voiture:
class Vehicule<VehiculeType> {
var vehiculeType:VehiculeType.Type
required init(type:VehiculeType.Type) {
self.vehiculeType = type
}
}
protocol Voiture {}
let voiture = Vehicule(type:Voiture.self)
--> Resultat dans un playground = Vehicule<Voiture>
Maintenant, et c'est là ma question, je voudrais créer un type spécialisé, non pas en spécifiant explicitement le type, mais avec un initialisateur dédié comme par exemple:
let voiture5Portes = Vehicule(voitureAvecPortes:5)
Je modifie donc mon code comme suit:
protocol Voiture {}
class Vehicule<VehiculeType> {
var vehiculeType:VehiculeType.Type
var nombreDePortes:Int?
required init(type:VehiculeType.Type) {
self.vehiculeType = type
}
convenience init(voitureAvecPortes nbportes:Int) {
self.init(type:Voiture.self as! VehiculeType.Type)
self.nombreDePortes = nbportes
}
}
let cinqPortes = Vehicule(voitureAvecPortes: 5)
D'abord, j'ai été obligé de caster Voiture.self en Vehicule.Type pour que le compiler me laisse tranquille, alors que dans la version précédente, je n'en avais pas besoin.
Ensuite, le compiler coince quand j'essaie de créer la voiture à la dernière ligne:
Generic Parameter 'VehiculeType' could not be inferred
Pourquoi donc alors que le VehiculeType est spécifié explicitement comme étant une Voiture ?!
Merci pour vos lumières.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pourquoi tu voudrais faire une chose pareille ?
La geÌneÌriciteÌ ne te suffit pas ? Tu essaie de faire quoi ?
Ce n'est sans doute pas assez explicite, il faut aller voir dans la deuxième méthode init pour trouver un endroit où le paramètre de la classe est utilisé.
Je ne sais pas quelles sont les règles qui permettent au compilateur d'inférer le type d'une classes générique, mais je peux comprendre que ton deuxième exemple est trop complexe à analyser pour le compilateur.
A priori je dirai que l'inférence ne fonctionne que si le paramètre de la classe est présent dans les paramètres du constructeur.
Certaines API existantes fonctionnent comme cela ( sans préjuger de l'implementation).
Par exemple, NSExpression dans Foundation. On peut initialiser une expression comme ceci:
Ou bien:
(dans le dernier cas c'est une class function mais cela ne change rien au pb).
Ces deux instances partagent une même fonction expressionValueWithObject(object:Any?, context:Dictionary) -> Any? mais les signatures diffèrent selon le type d'expression:
Une ConstantValueExpression renverra toujours la constante avec laquelle on l'a initialisée:
alors qu'une expression EvaluatedObjectExpression renverra l'objet évalué:
Mon idée était de créer des versions spécialisées à l'initialisation puis d'implémenter -evaluateWithObject dans des protocol extensions ciblés avec une where clause.
Donc pour répondre à la question, voilà ce que j'essaie de faire : NSExpression en swift avec des génériques.
Pour info: en Objective-C NSExpression est une classe abstraite (class cluster). Ce serait un bon exemple d'adaptation d'un class cluster en swift, sachant que swift privilégie les initializers aux class functions.
Normalement, avec les Generics, on ferait :
Ou, peut-être :
Mais je crois que ce serait relativement inutile.
D'autant qu'en regardant au code de NSExpression tu n'as aucunement besoin de faire une telle chose...
Tu maniple des NSArray, des NSNumber et des NSString, un enum et on est bon. Je pense que tu essaie de trop te compliquer la vie sur le coup là.
Et je ne vois pas trop en quoi NSExpression serait une classe abstraite. (Je suis prêt à te croire hein mais a priori pour moi ça l'est pas).
Juré, NSExpression est une classe abstraite en Objective-C. Quand on fait [NSExpression expressionForConstantValue:@o], on n'obtient pas une NSExpression mais une sous-classe.
Cela étant, je suis d'accord avec toi pour dire qu'on peut implementer NSExpression sans generics. D'ailleurs je l'ai fait.
Mais là je voudrais bénéficier du typage fort de swift.
Le fait de connaitre à l'avance le type du résultat de l'évaluation est un gros avantage par rapport à l'Objective-C en terme de type checking et de performance.
Par exemple, en swift pur, quand on fait NSExpression(forConstantValue:1).expressionValueWithObject(nil, context:nil), le type checkeur devrait supposer qu'on obtient un Int.
Ou alors NSExpression(forConstantValue:1).variable devrait donner une erreur de compilation, ce qui n'est pas le cas en OC.
Et comme les NSPredicate sont composés de NSExpression comparées au moment de l'évaluation, le type checkeur devrait aussi refuser des expressions non comparables. Il serait impossible de construire un predicate "o" < 1 alors qu'en OC c'est possible et ça bogue plus tard.
Donc je pense que ça vaudrait le coup de pousser Swift dans ses retranchements pour améliorer l'implementation OC. Et aussi pour mieux connaitre Swift et toute sa bande de (sales?) types.
J'ai essayé ça
Et maintenant Monsieur Compilo me dit String is not convertible to Any?
Il est dingue ce type !
Hello,
Je pense que ça vient de ton extension sur NSExpressionProtocol et la contrainte que tu lui appliques.
Ici le type génériques ExpressionType doit être égale à Constant mais dans ton code il est inféré a ExpressionTypeProtocol.
Même si Constant se conforme a ExpressionProtocol ça reste deux types différents.