2016-10-14 2 views
5

Je voudrais demander un type d'objet de collection (type de réflexion spécifique). Mais en ce qui concerne l'effacement de type il semble pas possible et aussi en ce qui concerne certains sujets que j'ai lus ici sur la pile. Il y a quelques solutions de contournement (here), mais je suis curieux de savoir si quelqu'un sait comment est-il fait par exemple par DWR:Comment DWR jette les données entrantes et évite l'effacement du type

http://directwebremoting.org/dwr/documentation/server/configuration/dwrxml/signatures.html

ou dans le cas où il y a une meilleure solution que ce serait formidable.

Disons que nous avons quelque chose comme:

public String foo(List<String> possibleFoos) { 

et tout ce que je dois est de savoir que le paramètre possibleFoos est la liste des chaînes, non seulement la liste

Répondre

6

Il est vrai que Java efface types à runtime (transformant ainsi List<String> en List), dans la plupart des cas, il stocke le type générique en cours d'exécution, ce qui vous permet de restaurer les informations perdues lors de l'effacement. Vous pouvez récupérer les types génériques réels pour ces:

  • arguments de méthode (votre cas)
  • types de retour de la méthode
  • types de terrain
  • super classes/interfaces

Cela signifie que si vous avez simplement un objet de type Liste, il n'y a rien que vous pouvez faire pour obtenir son type générique (object.getClass() vous obtiendrez List et c'est tout) - il a été pe Manifestement perdu. Mais, si vous essayez de comprendre le type générique pour un argument de méthode ou l'un de ce qui précède, vous pouvez en utilisant la réflexion. En tant que votre cas ne comporte pas de variables ou d'autres complications, il est assez simple de type pour obtenir le type réel:

ParameterizedType listArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", List.class).getGenericParameterTypes()[0]; 
Type listType = listArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the List 

Si vous aviez un plusieurs paramètres et une carte à la place:

public String foo(Object bar, Map<String, Number> possibleFoos) { ... } 

Le code renverrait être similaire:

ParameterizedType mapArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", Object.class, Map.class).getGenericParameterTypes()[1]; //1 is the index of the map argument 
Type keyType = mapArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the Map key 
Type valType = mapArgument.getActualTypeArguments()[1]; //This will be a Class representing Number, the type of the Map value 

Il est sûr de supposer que c'est ce que DWR utilise aussi bien, car les types sont des arguments de la méthode.

méthodes similaires sont disponibles pour d'autres cas énumérés:

  • Class.getMethod(...).getGenericReturnType() vous obtiendrez le type réel de retour
  • Class.getField(fieldName).getGenericType() vous obtiendrez le vrai type du champ
  • Class.getGenericSuperClass() vous obtiendrez le vrai super tapez
  • Class.getGenericInterfaces() vous obtiendrez les types d'interface réelle

Des méthodes équivalentes existent permettant l'accès à AnnotatedType (type générique ainsi que des annotations sur l'utilisation du type) introduit en Java 8:

  • Class.getMethod(...).getAnnotatedParameterTypes()
  • Class.getMethod(...).getAnnotatedReturnType()
  • Class.getField(fieldName).getAnnotatedType()
  • Class.getAnnotatedSuperClass()
  • Class.getAnnotatedInterfaces()

Maintenant, tout cela est dandy lorsque votre cas est aussi simple que dans l'exemple. Mais, imaginez si votre exemple ressemblait à ceci:

public T foo(List<T> possibleFoos) {...} 

Dans ce cas, getGenericParameterTypes()[0].getActualTypeArguments()[0] vous donnerait T ce qui est plutôt inutile. Pour résoudre ce que T signifie, vous devez regarder dans la définition de classe, et peut-être super classes, tout en gardant une trace de la façon dont les variables de type sont nommés dans chaque classe, car les noms pourraient être différents dans chacun.

Pour rendre le travail avec la réflexion de type générique plus facile, vous pouvez utiliser une merveilleuse bibliothèque appelée GenTyRef qui fait le travail pour vous, et si vous avez besoin d'aide pour AnnotatedType s, vous pouvez utiliser ma fourchette appelé GeAnTyRef (les deux sont dans Maven Central). Ils incluent également une fabrique de types, qui vous permet de construire des instances de (Annotated)Type, ce que vous ne pouvez pas faire facilement en utilisant l'API Java normale. Il existe également une implémentation pratique super type token vous permettant d'obtenir un littéral (Annotated)Type.

Avec ceux-ci, vous pouvez tout faire avec des types génériques Java permet sans les tracas je l'ai expliqué ci-dessus:

  • GenericTypeReflector#getExactParameterTypes(...)
  • GenericTypeReflector#getExactReturnType(...)
  • GenericTypeReflector#getExactFieldType(...)
  • GenericTypeReflector#getExactSuperType(...)

Et beaucoup plus d'opérations, comme figuri ng out si un Type est un super type d'un autre (similaire à Class#isAssignableFrom mais pour les types génériques), résolvant des variables de type spécifiques, etc.

+0

Merci pour votre réponse complète, c'est exactement ce que je voulais savoir! – Radim

+0

J'ai utilisé la version "hassle" et cela fonctionne très bien pour la liste, mais cela ne fonctionne pas pour map, car getParameterizedType retourne l'objet class.Depuis que j'ai un meilleur aperçu de votre post précédent, j'ai essayé de chercher à nouveau comment utiliser la carte, mais je n'ai rien trouvé. Pouvez-vous m'aider une fois de plus s'il vous plaît? – Radim

+1

@Radim J'ai mis à jour ma réponse pour inclure un exemple qui a une carte comme deuxième argument. C'est ce que vous recherchez? – kaqqao