web-dev-qa-db-fra.com

Comment créer un littéral de classe d'un type connu: Class <List <String >>

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.

55
skaffman

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.

40
Bozho

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

15
Kevin Bourrillion

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);
}
7
finnw

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();
6
JRL

Vous pouvez implémenter cette méthode comme ceci:

public Class<List<String>> getObjectType() {
    return (Class<List<String>>) ((Class)List.class);
}
5
Saeed Zarinfam

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":

http://gafter.blogspot.com/2006/12/super-type-tokens.html

2
Mike Tunnicliffe

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.

1
perdian

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];
}
0
Lukas Z.

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

0
Yaneeve

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>
0
mazi