Leitfaden zu den Reflection Utilities von Guava

1. Übersicht

In diesem Artikel beschäftigen wir uns mit der Guava- Reflection- API, die im Vergleich zur Standard-Java-Reflection-API definitiv vielseitiger ist.

Wir werden Guava verwenden , um generische Typen zur Laufzeit zu erfassen, und wir werden Invokable auch gut nutzen.

2. Erfassen des generischen Typs zur Laufzeit

In Java werden Generika mit Typlöschung implementiert. Das bedeutet, dass die generischen Typinformationen nur zur Kompilierungszeit verfügbar sind und zur Laufzeit nicht mehr verfügbar sind.

Beispiel: Liste: Die Informationen zum generischen Typ werden zur Laufzeit gelöscht. Aufgrund dieser Tatsache ist es nicht sicher , um generische passieren Klasse zur Laufzeit - Objekte.

Möglicherweise weisen wir derselben Referenz zwei Listen mit unterschiedlichen generischen Typen zu, was eindeutig keine gute Idee ist:

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

Aufgrund der Typlöschung kann die Methode isAssignableFrom () den tatsächlichen generischen Typ der Listen nicht kennen. Grundsätzlich werden zwei Typen verglichen, die nur eine Liste ohne Informationen über den tatsächlichen Typ sind.

Mithilfe der Standard-Java-Reflection-API können wir die generischen Arten von Methoden und Klassen erkennen. Wenn wir eine Methode haben, die eine Liste zurückgibt , können wir Reflection verwenden, um den Rückgabetyp dieser Methode zu erhalten - einen ParameterizedType, der List darstellt .

Die TypeToken- Klasse verwendet diese Problemumgehung , um die Manipulation generischer Typen zu ermöglichen. Wir können die TypeToken- Klasse verwenden, um einen tatsächlichen Typ einer generischen Liste zu erfassen und zu überprüfen, ob sie tatsächlich mit derselben Referenz referenziert werden können:

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

Nur das integerListToken kann einer Referenz vom Typ nubmerTypeToken zugewiesen werden, da eine Integer- Klasse eine Number- Klasse erweitert .

3. Erfassen komplexer Typen mit TypeToken

Angenommen, wir möchten eine generische parametrisierte Klasse erstellen und zur Laufzeit Informationen über einen generischen Typ erhalten. Wir können eine Klasse erstellen, die ein TypeToken als Feld hat, um diese Informationen zu erfassen:

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

Wenn Sie dann eine Instanz dieser Klasse erstellen, ist der generische Typ zur Laufzeit verfügbar:

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

Wir können auch ein TypeToken eines komplexen Typs mit mehr als einem generischen Typ erstellen und zur Laufzeit Informationen zu jedem dieser Typen abrufen:

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

Wir erhalten einen tatsächlichen Rückgabetyp für Function , dh einen String. Wir können sogar einen Typ des Eintrags in die Karte bekommen:

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

Hier verwenden wir eine Reflektionsmethode getMethod () aus der Java-Standardbibliothek, um den Rückgabetyp einer Methode zu erfassen.

4. Aufrufbar

Das Invokable ist ein fließender Wrapper von java.lang.reflect.Method und java.lang.reflect.Constructor . Es bietet eine einfachere API zusätzlich zu einer Standard-Java- Reflection- API. Nehmen wir an, wir haben eine Klasse mit zwei öffentlichen Methoden, von denen eine endgültig ist:

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

Nun wollen wir die untersuchen somePublicMethod () unter Verwendung von Guava API und Java Standard Reflexion API:

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

Es gibt keinen großen Unterschied zwischen diesen beiden Varianten, aber zu überprüfen, ob eine Methode überschreibbar ist, ist in Java eine wirklich nicht triviale Aufgabe. Glücklicherweise macht es die isOverridable () -Methode aus der Invokable- Klasse einfacher:

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

Wir sehen , dass selbst eine so einfache Bedienung viele Kontrollen unter Verwendung von Standard muss Reflexion API. Die Invokable- Klasse verbirgt dies hinter der API, die einfach zu verwenden und sehr präzise ist.

5. Schlussfolgerung

In diesem Artikel haben wir uns die Guava Reflection API angesehen und sie mit dem Standard-Java verglichen. Wir haben gesehen, wie generische Typen zur Laufzeit erfasst werden und wie die Invokable- Klasse eine elegante und benutzerfreundliche API für Code bietet, der Reflection verwendet.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie im GitHub-Projekt - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.