Considérez cet exemple (typique dans les livres OOP):
J'ai une Animal
classe, où chacun Animal
peut avoir beaucoup d'amis.
Et les sous - classes aiment Dog
, Duck
, Mouse
etc qui ajoutent des comportements spécifiques comme bark()
, quack()
etc.
Voici la Animal
classe:
public class Animal {
private Map<String,Animal> friends = new HashMap<>();
public void addFriend(String name, Animal animal){
friends.put(name,animal);
}
public Animal callFriend(String name){
return friends.get(name);
}
}
Et voici un extrait de code avec beaucoup de transtypage:
Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());
((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();
Existe-t-il un moyen d'utiliser des génériques pour le type de retour pour me débarrasser de la conversion de type, afin que je puisse dire
jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();
Voici un code initial avec un type de retour transmis à la méthode en tant que paramètre qui n'est jamais utilisé.
public<T extends Animal> T callFriend(String name, T unusedTypeObj){
return (T)friends.get(name);
}
Existe-t-il un moyen de déterminer le type de retour à l'exécution sans utiliser le paramètre supplémentaire instanceof
? Ou au moins en passant une classe du type au lieu d'une instance factice.
Je comprends que les génériques sont destinés à la vérification du type de compilation, mais existe-t-il une solution à cela?
la source
Non. Le compilateur ne peut pas savoir quel type
jerry.callFriend("spike")
retournerait. En outre, votre implémentation masque simplement le casting dans la méthode sans aucune sécurité de type supplémentaire. Considère ceci:Dans ce cas spécifique, créer une
talk()
méthode abstraite et la remplacer de manière appropriée dans les sous-classes vous servirait beaucoup mieux:la source
Vous pouvez l'implémenter comme ceci:
(Oui, il s'agit d'un code légal; voir Java Generics: type générique défini comme type de retour uniquement .)
Le type de retour sera déduit de l'appelant. Cependant, notez l'
@SuppressWarnings
annotation: cela vous indique que ce code n'est pas de type sécurisé . Vous devez le vérifier vous-même, ou vous pourriez l'obtenirClassCastExceptions
au moment de l'exécution.Malheureusement, la façon dont vous l'utilisez (sans affecter la valeur de retour à une variable temporaire), la seule façon de rendre le compilateur heureux est de l'appeler comme ceci:
Bien que cela puisse être un peu plus agréable que le casting, vous feriez probablement mieux de donner à la
Animal
classe unetalk()
méthode abstraite , comme l'a dit David Schmitt.la source
jerry.CallFriend<Dog>(...
je pense que c'est mieux.java.util.Collections.emptyList()
fonction du JRE est implémentée exactement comme ceci, et son javadoc se présente comme étant de type sécurisé.Cette question est très similaire à l' article 29 de Java efficace - «Considérez les conteneurs hétérogènes sécuritaires». La réponse de Laz est la plus proche de la solution de Bloch. Cependant, put et get doivent utiliser le littéral Class pour plus de sécurité. Les signatures deviendraient:
Dans les deux méthodes, vous devez vérifier que les paramètres sont raisonnables. Voir Java efficace et la classe javadoc pour plus d'informations.
la source
De plus, vous pouvez demander à la méthode de renvoyer la valeur dans un type donné de cette façon
Plus d'exemples ici dans la documentation Oracle Java
la source
Voici la version la plus simple:
Code entièrement fonctionnel:
la source
Casting to T not needed in this case but it's a good practice to do
. Je veux dire, si elle est correctement gérée pendant l'exécution, que signifie «bonne pratique»?Comme vous avez dit que passer un cours serait OK, vous pouvez écrire ceci:
Et puis utilisez-le comme ceci:
Pas parfait, mais c'est à peu près aussi loin que vous obtenez avec les génériques Java. Il existe un moyen d'implémenter des conteneurs hétérogènes Typesafe (THC) à l'aide de jetons Super Type , mais cela a à nouveau ses propres problèmes.
la source
Basé sur la même idée que les Super Type Tokens, vous pouvez créer un identifiant typé à utiliser à la place d'une chaîne:
Mais je pense que cela peut aller à l'encontre du but, car vous devez maintenant créer de nouveaux objets id pour chaque chaîne et les conserver (ou les reconstruire avec les informations de type correctes).
Mais vous pouvez maintenant utiliser la classe comme vous le vouliez à l'origine, sans les transtypages.
Cela cache simplement le paramètre type à l'intérieur de l'id, bien que cela signifie que vous pouvez récupérer le type de l'identifiant plus tard si vous le souhaitez.
Vous devez également implémenter les méthodes de comparaison et de hachage de TypedID si vous souhaitez pouvoir comparer deux instances identiques d'un identifiant.
la source
"Existe-t-il un moyen de déterminer le type de retour à l'exécution sans le paramètre supplémentaire en utilisant instanceof?"
Comme solution alternative, vous pouvez utiliser le modèle de visiteur comme celui-ci. Rendre Animal abstrait et le rendre implémentable Visible:
Visible signifie simplement qu'une implémentation Animal est prête à accepter un visiteur:
Et une implémentation visiteur est capable de visiter toutes les sous-classes d'un animal:
Ainsi, par exemple, une implémentation Dog ressemblerait à ceci:
L'astuce ici est que, comme le chien sait de quel type il s'agit, il peut déclencher la méthode de visite surchargée pertinente du visiteur v en passant "this" comme paramètre. D'autres sous-classes implémenteraient accept () exactement de la même manière.
La classe qui souhaite appeler des méthodes spécifiques à la sous-classe doit ensuite implémenter l'interface Visitor comme ceci:
Je sais que c'est beaucoup plus d'interfaces et de méthodes que ce que vous aviez négocié, mais c'est un moyen standard d'obtenir une poignée sur chaque sous-type spécifique avec précisément zéro instanceof vérifications et zéro type cast. Et tout est fait d'une manière indépendante du langage standard, donc ce n'est pas seulement pour Java, mais tout langage OO devrait fonctionner de la même manière.
la source
Pas possible. Comment la carte est-elle censée savoir quelle sous-classe d'animal elle obtiendra, étant donné uniquement une clé de chaîne?
La seule manière possible serait que chaque animal n'accepte qu'un seul type d'ami (alors ce pourrait être un paramètre de la classe Animal), ou que la méthode callFriend () obtienne un paramètre de type. Mais il semble vraiment que vous manquez le point d'héritage: c'est que vous ne pouvez traiter les sous-classes de manière uniforme qu'en utilisant exclusivement les méthodes de superclasse.
la source
J'ai écrit un article qui contient une preuve de concept, des classes de support et une classe de test qui montre comment les Super Type Tokens peuvent être récupérés par vos classes lors de l'exécution. En un mot, il vous permet de déléguer à des implémentations alternatives en fonction des paramètres génériques réels passés par l'appelant. Exemple:
TimeSeries<Double>
délègue à une classe interne privée qui utilisedouble[]
TimeSeries<OHLC>
délègue à une classe interne privée qui utiliseArrayList<OHLC>
Voir: Utilisation de TypeTokens pour récupérer des paramètres génériques
Merci
Richard Gomes - Blog
la source
Il y a beaucoup de bonnes réponses ici, mais c'est l'approche que j'ai adoptée pour un test Appium où agir sur un seul élément peut entraîner le passage à différents états d'application en fonction des paramètres de l'utilisateur. Bien qu'il ne respecte pas les conventions de l'exemple de OP, j'espère que cela aide quelqu'un.
Si vous ne voulez pas jeter les erreurs, vous pouvez les attraper comme ceci:
la source
Pas vraiment, car comme vous le dites, le compilateur sait seulement que callFriend () renvoie un animal, pas un chien ou un canard.
Ne pouvez-vous pas ajouter une méthode abstraite makeNoise () à Animal qui serait implémentée sous forme d'écorce ou de charlatan par ses sous-classes?
la source
Ce que vous cherchez ici, c'est l'abstraction. Codez plus contre les interfaces et vous devriez faire moins de cast.
L'exemple ci-dessous est en C # mais le concept reste le même.
la source
J'ai fait ce qui suit dans mon lib kontraktor:
sous-classement:
au moins, cela fonctionne à l'intérieur de la classe actuelle et avec une référence typée forte. L'héritage multiple fonctionne, mais devient alors très délicat :)
la source
Je sais que c'est une chose complètement différente que celle demandée. Une autre façon de résoudre ce problème serait la réflexion. Je veux dire, cela ne profite pas des génériques, mais cela vous permet d'émuler, en quelque sorte, le comportement que vous souhaitez effectuer (faire un aboiement de chien, faire un charlatan de canard, etc.) sans prendre soin du casting de type:
la source
qu'en est-il de
}
la source
Il existe une autre approche, vous pouvez restreindre le type de retour lorsque vous remplacez une méthode. Dans chaque sous-classe, vous devrez remplacer callFriend pour renvoyer cette sous-classe. Le coût serait les déclarations multiples de callFriend, mais vous pourriez isoler les parties communes d'une méthode appelée en interne. Cela me semble beaucoup plus simple que les solutions mentionnées ci-dessus, et n'a pas besoin d'un argument supplémentaire pour déterminer le type de retour.
la source
public int getValue(String name){}
est impossible de distinguer dupublic boolean getValue(String name){}
point de vue des compilateurs. Vous devrez soit changer le type de paramètre, soit ajouter / supprimer des paramètres pour que la surcharge soit reconnue. Peut-être que je vous comprends mal ...Vous pouvez retourner n'importe quel type et recevoir directement comme. Pas besoin de transtyper.
C'est mieux si vous souhaitez personnaliser tout autre type d'enregistrements au lieu de vrais curseurs.
la source
(Cursor) cursor
par exemple.cursor
être unCursor
, et retournera toujours unPerson
(ou null). L'utilisation de génériques supprime la vérification de ces contraintes, rendant le code dangereux.