Guide des utilitaires de réflexion de Guava

1. Vue d'ensemble

Dans cet article, nous examinerons l' API de réflexion Guava - qui est nettement plus polyvalente que l'API de réflexion Java standard.

Nous utiliserons Guava pour capturer des types génériques au moment de l'exécution, et nous ferons également bon usage d' Invokable .

2. Capture de type générique à l'exécution

En Java, les génériques sont implémentés avec l'effacement de type. Cela signifie que les informations de type générique ne sont disponibles qu'au moment de la compilation et, au moment de l'exécution, elles ne sont plus disponibles.

Par exemple, List, les informations sur le type générique sont effacées lors de l'exécution. De ce fait, il n'est pas sûr de transmettre des objets de classe génériques au moment de l'exécution.

Nous pourrions finir par attribuer deux listes qui ont des types génériques différents à la même référence, ce qui n'est clairement pas une bonne idée:

List stringList = Lists.newArrayList(); List intList = Lists.newArrayList(); boolean result = stringList.getClass() .isAssignableFrom(intList.getClass()); assertTrue(result);

En raison de l'effacement de type, la méthode isAssignableFrom () ne peut pas connaître le type générique réel des listes. Il compare essentiellement deux types qui ne sont qu'une liste sans aucune information sur le type réel.

En utilisant l'API de réflexion Java standard, nous pouvons détecter les types génériques de méthodes et de classes. Si nous avons une méthode qui renvoie un List , nous pouvons utiliser la réflexion pour obtenir le type de retour de cette méthode - un ParameterizedType représentant List .

La classe TypeToken utilise cette solution de contournement pour permettre la manipulation de types génériques. Nous pouvons utiliser la classe TypeToken pour capturer un type réel de liste générique et vérifier si elles peuvent vraiment être référencées par la même référence:

TypeToken
    
      stringListToken = new TypeToken
     
      () {}; TypeToken
      
        integerListToken = new TypeToken
       
        () {}; TypeToken
        
          numberTypeToken = new TypeToken
         
          () {}; assertFalse(stringListToken.isSubtypeOf(integerListToken)); assertFalse(numberTypeToken.isSubtypeOf(integerListToken)); assertTrue(integerListToken.isSubtypeOf(numberTypeToken));
         
        
       
      
     
    

Seul le integerListToken peut être affecté à une référence de type nubmerTypeToken car une classe Integer étend une classe Number .

3. Capture de types complexes à l'aide de TypeToken

Disons que nous voulons créer une classe paramétrée générique et que nous voulons avoir des informations sur un type générique au moment de l'exécution. Nous pouvons créer une classe qui a un TypeToken comme champ pour capturer ces informations:

abstract class ParametrizedClass { TypeToken type = new TypeToken(getClass()) {}; }

Ensuite, lors de la création d'une instance de cette classe, le type générique sera disponible au moment de l'exécution:

ParametrizedClass parametrizedClass = new ParametrizedClass() {}; assertEquals(parametrizedClass.type, TypeToken.of(String.class));

Nous pouvons également créer un TypeToken d'un type complexe qui a plus d'un type générique, et récupérer des informations sur chacun de ces types au moment de l'exécution:

TypeToken
    
      funToken = new TypeToken
     
      () {}; TypeToken funResultToken = funToken .resolveType(Function.class.getTypeParameters()[1]); assertEquals(funResultToken, TypeToken.of(String.class));
     
    

Nous obtenons un type de retour réel pour Function , c'est-à-dire une chaîne. Nous pouvons même obtenir un type de l'entrée dans la carte:

TypeToken mapToken = new TypeToken() {}; TypeToken entrySetToken = mapToken .resolveType(Map.class.getMethod("entrySet") .getGenericReturnType()); assertEquals( entrySetToken, new TypeToken
      
       >() {}); 
      

Ici, nous utilisons une méthode de réflexion getMethod () de la bibliothèque standard Java pour capturer le type de retour d'une méthode.

4. Invocable

Le Invokable est un wrapper fluide de java.lang.reflect.Method et java.lang.reflect.Constructor . Il fournit une API plus simple en plus d'une API de réflexion Java standard . Disons que nous avons une classe qui a deux méthodes publiques et l'une d'elles est finale:

class CustomClass { public void somePublicMethod() {} public final void notOverridablePublicMethod() {} }

Examinons maintenant somePublicMethod () à l' aide de l'API Guava et de l' API de réflexion standard Java :

Method method = CustomClass.class.getMethod("somePublicMethod"); Invokable invokable = new TypeToken() {} .method(method); boolean isPublicStandradJava = Modifier.isPublic(method.getModifiers()); boolean isPublicGuava = invokable.isPublic(); assertTrue(isPublicStandradJava); assertTrue(isPublicGuava);

Il n'y a pas beaucoup de différence entre ces deux variantes, mais vérifier si une méthode peut être remplacée est une tâche vraiment non triviale en Java. Heureusement, la méthode isOverridable () de la classe Invokable facilite les choses:

Method method = CustomClass.class.getMethod("notOverridablePublicMethod"); Invokable invokable = new TypeToken() {}.method(method); boolean isOverridableStandardJava = (!(Modifier.isFinal(method.getModifiers()) || Modifier.isPrivate(method.getModifiers()) || Modifier.isStatic(method.getModifiers()) || Modifier.isFinal(method.getDeclaringClass().getModifiers()))); boolean isOverridableFinalGauava = invokable.isOverridable(); assertFalse(isOverridableStandardJava); assertFalse(isOverridableFinalGauava);

Nous voyons que même une opération aussi simple nécessite de nombreuses vérifications à l'aide de l' API de réflexion standard . La classe Invokable cache cela derrière l'API qui est simple à utiliser et très concise.

5. Conclusion

Dans cet article, nous avons examiné l'API de réflexion Guava et l'avons comparée à Java standard. Nous avons vu comment capturer des types génériques au moment de l'exécution et comment la classe Invokable fournit une API élégante et facile à utiliser pour le code utilisant la réflexion.

L'implémentation de tous ces exemples et extraits de code se trouve dans le projet GitHub - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.