J'ai vu un certain nombre de questions similaires, mais je ne pense pas qu'elles étaient tout à fait isomorphes, et aucune n'a tout à fait répondu à ma question.
Supposons qu'il y ait deux interfaces, Tree
et Named
. Supposons en outre que l'on me donne une méthode dont la signature est
public <T extends Tree & Named> T getNamedTree();
Comment puis-je enregistrer la valeur renvoyée dans une variable, tout en conservant les informations qu'elle implémente à la fois Tree
et Named
? Je ne trouve pas de moyen de déclarer une variable comme
public <T extends Tree & Named> T mNamedTree;
Et essayer de le convertir en une interface étendant Tree
et Named
entraîne une exception de conversion de classe.
3 réponses
Quelle est la portée de la variable?
Il y a trois possibilités ici.
A) la variable n'est qu'une variable locale. Dans ce cas, vous avez presque déjà la réponse ... il vous suffit de déclarer un paramètre de type pour la méthode englobante pour ce type:
interface ItfA { Number propA(); };
interface ItfB { Number propB(); };
class Main {
private <T extends ItfA & ItfB> T getT() {
return null;
}
private <TT extends ItfA & ItfB> void doStuffWithT() {
TT theT = getT();
System.err.println(theT.propA());
System.err.println(theT.propB());
}
}
B) La portée est la vie d'un objet et dans ce cas est un champ membre. La réponse évidente est de rendre la classe générique et le paramètre de type ont la même contrainte &
:
interface ItfA { Number propA(); };
interface ItfB { Number propB(); };
class Main<T extends ItfA & ItfB> {
T theT;
public void setT(T newT) {
theT = newT;
}
public void doStuffWithT() {
System.err.println(theT.propA());
System.err.println(theT.propB());
}
}
C) La portée est le live du programme, alors la variable est un membre de classe statique. Ici, vous n'avez pas de solution générique.
C.1) Evidemment, si la classe des valeurs que vous allez gérer est connue, vous utiliserez simplement cette classe comme type de champ.
C.2) Sinon, vous pouvez contraindre le code à gérer uniquement les classes qui implémentent une interface qui étend ItfA et ItfB. Cette interface, dites ItfAB
. Serait de type de champ.
C.3) Maintenant, qu'en est-il de ne pas imposer cette contrainte? Qu'en est-il de permettre au code de gérer les objets de n'importe quelle classe qui implémentent ces interfaces?
Malheureusement, il n'existe pas de solution claire à cela:
C.3.a) Vous pouvez taper le champ Object
et fournir des méthodes pour y accéder comme ItfA ou comme ItfB (masquant essentiellement le casting).
C.3.b) Ou, au lieu de contenir directement une référence à l'objet, vous utilisez un objet proxy qui implémente ces interfaces et délègue les appels à ces méthodes d'interface à la valeur typée d'origine "T
". La classe de ce proxy pourrait elle-même être un générique acceptant une valeur <T extends ItfA & ItfB>
arbitraire (similaire à l'exemple B. ci-dessus).
En supposant qu'aucune troisième interface n'hérite à la fois de Named
et de Tree
, vous ne pouvez pas conserver les informations sur les deux interfaces de manière statique. Le compilateur vous demandera de faire un cast pour l'un ou l'autre, ou pour les deux:
Object namedTree = getNamedTree();
Tree asTree = (Tree)namedTree;
Named asNamed = (Named)namedTree;
Les deux lancers devraient réussir.
Si vous avez une influence sur la conception de l'API pour la classe, demandez aux auteurs d'introduire une interface combinant à la fois Named
et Tree
, et renvoyant à la place une instance de cette interface.
Une solution possible serait de créer un autre interface
qui extends
à la fois Tree
et Named
, et simplement le stocker comme variable:
interface NamedTree extends Tree, Named {
}
public NamedTree namedTree;
public NamedTree getNamedTree();
Questions connexes
De nouvelles questions
java
Java est un langage de programmation de haut niveau. Utilisez cette balise lorsque vous rencontrez des problèmes pour utiliser ou comprendre la langue elle-même. Cette balise est rarement utilisée seule et est le plus souvent utilisée en conjonction avec [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] et [maven].