Prenez ce qui suit:
public Class<List<String>> getObjectType() {
// what can I return here?
}
Quelle expression littérale de classe puis-je retourner de cette méthode qui satisfera les génériques et compilera? List.class
ne compilera pas, ni List.<String>class
.
Si vous vous demandez "pourquoi", j'écris une implémentation du FactoryBean<List<String>>
de Spring, qui nécessite que je mette en œuvre Class<List<String>> getObjectType()
. Cependant, ceci est pas une question de printemps.
edit: Mes cris plaintifs ont été entendus par les pouvoirs de SpringSource. Spring 3.0.1 aura donc le type de retour getObjectType()
remplacé par Class<?>
, ce qui évite parfaitement le problème.
Vous pouvez toujours choisir ce dont vous avez besoin, comme ceci
return (Class<List<String>>) new ArrayList<String>().getClass();
ou
return (Class<List<String>>) Collections.<String>emptyList().getClass();
Mais je suppose que ce n'est pas ce que vous recherchez. Eh bien cela fonctionne, avec un avertissement, mais ce n'est pas exactement "beau".
Je viens de trouver ça
Pourquoi n'y a-t-il pas de littéral de classe pour les types paramétrés génériques?
Parce qu'un type paramétré générique n'a pas de représentation exacte du type à l'exécution.
Donc, le casting pourrait être la seule voie à suivre.
Vous ne devriez jamais utiliser la construction Class<List<String>>
. C'est absurde, et devrait produire un avertissement en Java (mais ne le fait pas). Les instances de classe représentent toujours des types bruts, vous pouvez donc avoir Class<List>
; c'est tout. Si vous voulez que quelque chose représente un type générique réifié comme List<String>
, vous avez besoin d'un "jeton de type super" comme celui que Guice utilise:
http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html
L'existence d'un Class<List<String>>
est intrinsèquement dangereux. Voici pourquoi:
// This statement generates a warning - for a reason...
Class<List<String>> unsafeListClass = (Class<List<String>>) (Class<?>) List.class;
List<Integer> integerList = new ArrayList<Integer>(); // Ok
integerList.add(42); // Ok
System.out.println(unsafeListClass.isInstance(integerList)); // Prints "true".
List<String> stringList =
unsafeListClass.cast(integerList); // Succeeds, with no warning!
stringList.add("Hello, World!"); // Also succeeds with no warning
for (int x: integerList) {
// Compiles without warning, but throws ClassCastException at runtime
System.out.println(100-x);
}
Trouvé ce lien sur springframework.org qui donne un aperçu.
Par exemple.
List<String> myList = new ArrayList<String>();
return (Class<List<String>>)myList.getClass();
Vous pouvez implémenter cette méthode comme ceci:
public Class<List<String>> getObjectType() {
return (Class<List<String>>) ((Class)List.class);
}
Découvrez cette discussion sur les forums Sun:
http://forums.Sun.com/thread.jspa?threadID=5253007
Et l'article de blog référencé qui décrit une solution de contournement à l'aide de "jetons de type super":
Je ne sais pas du tout si cela est possible, car tout littéral de classe sera compilé pour Class.forName(...)
et, comme cela se produit à l'exécution, il ne reste aucune information générique.
L'approche suivante est problématique:
> public Class<List<String>> getModelType() {
> return (Class<List<String>>) new ArrayList<String>().getClass();
> }
par exemple. si vous voulez tester si un objet dit de type
org.Eclipse.emf.common.util.BasicEList<String>
est de type
List<String>
basé sur le résultat de l'approche getModelType () susmentionnée, par exemple:
BasicEList<String> fromObject = ...;
if (getModelType().isAssignableFrom(fromObject.getClass())) {
transferFromModelToUi(getModelType().cast(fromObject));
}
elle aboutira à false alors qu'elle devrait l'être car les deux objets implémentent l'interface List (puisque getModelType () renvoie un objet Class de type List et non ArrayList).
Voici une approche qui a fonctionné pour moi (un peu fastidieux mais qui conduit à des résultats corrects dans l'exemple ci-dessus, pourrait être déplacé vers un initialiseur statique):
public Class<List<String>> getModelType() {
Class<?> arrayListClass = new ArrayList<String>().getClass();
Class<?>[] interfaces = arrayListClass.getInterfaces();
int index = 0;
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(List.class)) {
index = i;
break;
}
}
return (Class<List<String>>) interfaces[index];
}
Je ne suis pas sûr, mais la question suivante que j'ai posée pourrait vous intéresser ...
Paramètres de type génériques Java et opérations sur ces types
Et ça:
public class TestMain {
public static void main(String[] args) throws Exception {
Type type = TestMain.class.getMethod("dummy").getGenericReturnType();
System.out.println("type = " + type);
}
public List<Integer> dummy() {return null;}
}
Cela imprime:
type = Java.util.List<Java.lang.Integer>