Introspection et types paramétrés en Java

Plusieurs fois déjà, j’ai eu recours à l’introspection pour extraire la structure de donnée encapsulée dans une classe Java. Avec quelques méthodes classiques des classes Class et Field, on réussit à extraire pas mal de chose :

  1. Dans un objet de type Class, getDeclaredFields permet d’obtenir la liste des attributs de cette classe, et getSuperclass permet de naviguer dans les classes parentes de l’arbre d’héritage pour récupérer les attributs hérités.
  2. Dans un objet de type Field, getName() donne le nom de cet attribut et getDeclaringClass donne son type déclaré (l’instance pouvant être d’un type héritant de ce type déclaré).

Viennent ensuite les types paramétrés qui contiennent de l’information supplémentaire sur la structure de données. En effet un attribut de type List<Integer>, ne sera considéré que comme un attribut de type List avec les méthodes précédentes. Or, le fait que cette liste contiennent des entiers est une information importante. De même un attribut de type List<List<MaClasse>>, n’est pas une vulgaire liste, mais bien un élément complexe de la structure de donnée. Il est donc important de bien gérer ces types paramétrés lors d’une telle introspection.

Un premier coup d’œuil à la documentation nous attire sur la méthode getTypeParameters de la classe Class. Cependant, attention, cette méthode ne nous sera pas très utile car elle ne renseigne que le paramétrage d’une classe seule. Ainsi, lors de l’introspection d’une classe ayant un attribut de type List<Integer>, les seules informations récupérées avec cette méthode seront que la classe de cet attribut, qui est List<E>, a un type paramétré qui s’appelle E. On n’est pas très avancé pour ce que l’on veut faire.

Il faut donc se tourner vers la déclaration de ces attributs, et travailler avec nos objets Field. Dans un premier temps, il faut récupérer le type (de classe Type) de cet attribut avec la méthode getGenericType. Puis la méthode getActualTypeArguments va nous informer très concrètement de la nature du paramétrage de notre attribut (enfin !). Cette méthode nous renvoie donc un tableau, dont chaque élément peut être de cinq types différents héritant tous de Type, en fonction de la nature du type paramétré :

  1. Class : c’est le cas le plus simple, et certainement le plus classique. Cela signifie que votre type paramétré est tout simplement une classe. C’est l’exemple de notre attribut de type List<Integer>, le type paramétré est Integer.
  2. ParameterizedType : notre type paramétré est lui-même paramétré par un autre type. C’est le cas par exemple, d’un attribut qui serait de type List<List<Integer>>. Il ne reste donc plus qu’à analyser de la même manière les types paramétrés de ce type, et on pourra récupérer le type de base avec la méthode getRawType de la classe ParameterizedType
  3. WildcardType : notre type paramétré est exprimé avec un joker, du style <? extends Collection> ou <? super Collection>. On peut alors récupérer le type à partir duquel est exprimé ce joker (ici Collection) avec la méthode getLowerBounds ou getUpperBounds de la classe WildcardType
  4. GenericArrayType : notre type paramétré est de type tableau, ce qui arriverait si notre attribut était déclaré de type List<Integer []>. Nous pouvons récupérer le type des éléments du tableau avec la méthode getGenericComponentType de la classe GenericArrayType.
  5. TypeVariable : dans ce cas, le type paramétré est exprimé à l’aide d’une variable. Ce qui pourrait être le cas dans l’exemple suivant :
    public class MaClasse<T extends Collection> {
      private List<T> maListe;
      …

    On peut alors récupérer la limite des types possibles avec la méthode getBounds de la classe TypeVariable.

Pour terminer, quelques références pour approfondir le sujet :

WordPress database error: [Table 'bobuse.wp2_comments' doesn't exist]
SELECT * FROM wp2_comments WHERE comment_post_ID = '64' AND comment_approved = '1' ORDER BY comment_date

Laisser un commentaire