Est-il possible d'obtenir le type d'un paramètre générique?
Un exemple:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
java
generics
reflection
cimnine
la source
la source
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
ne sais pas quelle est la contrainte.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Je veux essayer de décomposer la réponse de @DerMike pour expliquer:
Premièrement, l'effacement de type ne signifie pas que le JDK élimine les informations de type au moment de l'exécution. C'est une méthode pour permettre à la vérification de type à la compilation et à la compatibilité des types à l'exécution de coexister dans le même langage. Comme ce bloc de code l'implique, le JDK conserve les informations de type effacées - elles ne sont tout simplement pas associées aux transtypages vérifiés et autres.
Deuxièmement, cela fournit des informations de type générique à une classe générique exactement un niveau au-dessus de la hiérarchie à partir du type concret en cours de vérification - c'est-à-dire qu'une classe parent abstraite avec des paramètres de type générique peut trouver les types concrets correspondant à ses paramètres de type pour une implémentation concrète d'elle-même qui en hérite directement . Si cette classe était non abstraite et instanciée, ou si l'implémentation concrète était de deux niveaux plus bas, cela ne fonctionnerait pas (bien qu'un peu de jimmying puisse la faire appliquer à n'importe quel nombre prédéterminé de niveaux au-delà d'un, ou jusqu'à la classe la plus basse avec X paramètres de type générique, et cetera).
Quoi qu'il en soit, à l'explication. Voici à nouveau le code, séparé en lignes pour plus de facilité:
Soit «nous» la classe abstraite avec des types génériques qui contient ce code. Lire ceci à peu près à l'envers:
...Et c'est à peu près tout. Nous renvoyons donc les informations de type de notre propre implémentation concrète en nous-mêmes, et les utilisons pour accéder à un handle de classe. nous pourrions doubler getGenericSuperclass () et passer à deux niveaux, ou éliminer getGenericSuperclass () et obtenir des valeurs pour nous-mêmes en tant que type concret (mise en garde: je n'ai pas testé ces scénarios, ils ne sont pas encore venus pour moi).
Cela devient délicat si vos enfants concrets sont à un nombre arbitraire de sauts, ou si vous êtes concret et non définitif, et surtout si vous vous attendez à ce que l'un de vos enfants (de profondeur variable) ait ses propres génériques. Mais vous pouvez généralement concevoir en fonction de ces considérations, ce qui vous permet de faire la plupart du temps.
J'espère que cela a aidé quelqu'un! Je reconnais que cet article est ancien. Je vais probablement couper cette explication et la garder pour d'autres questions.
la source
En fait, je l'ai fait fonctionner. Considérez l'extrait de code suivant:
J'utilise jdk 1.6
la source
Il existe une solution en fait, en appliquant l' astuce de la "classe anonyme" et les idées des Super Type Tokens :
Les mensonges trick dans la création d'une classe anonyme ,
new ArrayList<SpiderMan>() {}
dans le lieu de l'original (simple)new ArrayList<SpiderMan>()
. L'utilisation d'une classe anoymous (si possible) garantit que le compilateur conserve les informations sur l'argument de typeSpiderMan
donné au paramètre de typeList<?>
. Voilà!la source
En raison de l'effacement de type, le seul moyen de connaître le type de la liste serait de passer le type en tant que paramètre à la méthode:
la source
Annexe à la réponse de @ DerMike pour obtenir le paramètre générique d'une interface paramétrée (en utilisant la méthode #getGenericInterfaces () dans une méthode par défaut Java-8 pour éviter la duplication):
la source
Awesomeable<Beer>
. Dans ce cas, les informations de type sont conservées. Si vous passeznew Awesomable<Beer> ()
à la méthode, cela ne fonctionnera pas.Awesomable<Beer>
à la volée sans la définition explicite d'une sous-classe concrète commeEstrellaGalicia
dans ce cas, il sort toujours le type paramétré: Je l'ai exécuté maintenant:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> Le type est: ParameterizedStuff $ BeerNon, ce n'est pas possible. En raison de problèmes de compatibilité descendante, les génériques de Java sont basés sur l' effacement de type , par exemple au moment de l'exécution, tout ce que vous avez est un non générique
List
objet . Il y a des informations sur les paramètres de type au moment de l'exécution, mais elles résident dans les définitions de classe (c'est-à-dire que vous pouvez demander " quel type générique la définition de ce champ utilise-t-elle? "), Pas dans les instances d'objet.la source
Comme l'a souligné @bertolami, il n'est pas possible de nous un type de variable et d'obtenir sa valeur future (le contenu de la variable typeOfList).
Néanmoins, vous pouvez lui passer la classe en paramètre comme ceci:
C'est plus ou moins ce que fait Google lorsque vous devez passer une variable de classe au constructeur d' ActivityInstrumentationTestCase2 .
la source
Non, ce n'est pas possible.
Vous pouvez obtenir un type générique d'un champ étant donné qu'une classe est la seule exception à cette règle et même c'est un peu un hack.
Voir Connaître le type de générique en Java pour un exemple de cela.
la source
Vous pouvez obtenir le type d'un paramètre générique avec une réflexion comme dans cet exemple que j'ai trouvé ici :
la source
La réponse rapide à la question est non, vous ne pouvez pas, en raison de l'effacement de type générique Java.
La réponse la plus longue serait que si vous avez créé votre liste comme ceci:
Ensuite, dans ce cas, le type générique est conservé dans la superclasse générique de la nouvelle classe anonyme ci-dessus.
Ce n'est pas que je recommande de faire cela avec des listes, mais c'est une implémentation d'écouteur:
Et comme l'extrapolation des types génériques de super classes et de super interfaces change entre les JVM, la solution générique n'est pas aussi simple que certaines réponses pourraient le suggérer.
Voici maintenant que je l'ai fait.
la source
Ceci est impossible car les génériques en Java ne sont pris en compte qu'au moment de la compilation. Ainsi, les génériques Java ne sont qu'une sorte de pré-processeur. Cependant, vous pouvez obtenir la classe réelle des membres de la liste.
la source
J'ai codé cela pour les méthodes qui s'attendent à accepter ou à retourner
Iterable<?...>
. Voici le code:la source
Vous ne pouvez pas obtenir de paramètre générique à partir d'une variable. Mais vous pouvez à partir d'une déclaration de méthode ou de champ:
la source
Rien que pour moi, lire cet extrait de code était difficile, je l'ai juste divisé en 2 lignes lisibles:
Je voulais créer une instance du paramètre Type sans avoir de paramètres pour ma méthode:
la source
Voici une autre astuce. Utiliser un tableau vararg générique
la source
J'ai remarqué que beaucoup de gens se tournent vers la
getGenericSuperclass()
solution:Cependant, cette solution est sujette aux erreurs. Cela ne fonctionnera pas correctement s'il y a des génériques dans les descendants. Considère ceci:
Quel type aura
Bar.persistentClass
?Class<Integer>
? Non, ça le seraClass<Double>
. Cela se produira cargetClass()
renvoie toujours la classe la plus élevée, ce qui estBar
dans ce cas, et sa super classe générique estFoo<Double>
. Par conséquent, le type d'argument seraDouble
.Si vous avez besoin d'une solution fiable qui n'échoue pas, je peux en suggérer deux.
Guava
. Il a une classe qui a été fait exactement à cet effet:com.google.common.reflect.TypeToken
. Il gère très bien tous les cas d'angle et offre des fonctionnalités plus intéressantes. L'inconvénient est une dépendance supplémentaire. Étant donné que vous avez utilisé cette classe, votre code semble simple et clair, comme ceci:la source
Utilisation:
la source
toArray()
revientObject[]
. Vous ne pouvez pas et ne devriez pas dépendre du fait qu'il s'agisse d'un sous-type particulier.