Ръководство за помощни програми за отразяване на Guava

1. Общ преглед

В тази статия ще разгледаме API за отразяване на Guava - който определено е по-гъвкав в сравнение със стандартния API за отражение на Java.

Ще използваме Guava за улавяне на родови типове по време на изпълнение и ще използваме добре и Invokable .

2. Заснемане на родов тип по време на изпълнение

В Java генеричните препарати се прилагат с изтриване на типа. Това означава, че общата информация за типа е достъпна само по време на компилация, а по време на изпълнение - вече не е налична.

Например List, информацията за родовия тип се изтрива по време на изпълнение. Поради този факт не е безопасно да се прехвърлят общите обекти на Class по време на изпълнение.

В крайна сметка може да присвоим два списъка с различни родови типове на една и съща препратка, което очевидно не е добра идея:

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

Поради изтриването на типа методът isAssignableFrom () не може да знае действителния общ тип на списъците. Той по същество сравнява два типа, които са само списък, без никаква информация за действителния тип.

Използвайки стандартния API за отражение на Java, можем да открием родовите типове методи и класове. Ако имаме метод, който връща List , можем да използваме отражение, за да получим типа на връщане на този метод - ParameterizedType, представляващ List .

Класът TypeToken използва това решение, за да позволи манипулирането на общи типове. Можем да използваме класа TypeToken, за да уловим действителен тип родов списък и да проверим дали те наистина могат да бъдат препращани със същата препратка:

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

Само integerListToken може да бъде присвоен на препратка от тип nubmerTypeToken, защото клас Integer разширява клас Number .

3. Заснемане на сложни типове с помощта на TypeToken

Да кажем, че искаме да създадем общ параметризиран клас и искаме да имаме информация за родов тип по време на изпълнение. Можем да създадем клас, който има TypeToken като поле за улавяне на тази информация:

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

След това, когато създавате екземпляр на този клас, родовият тип ще бъде достъпен по време на изпълнение:

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

Също така можем да създадем TypeToken от сложен тип, който има повече от един общ тип, и да извлечем информация за всеки от тези типове по време на изпълнение:

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

Получаваме действителен тип връщане за Функция , който е String. Можем дори да получим тип запис в картата:

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

Тук използваме метод на отражение getMethod () от стандартната библиотека на Java, за да уловим типа на връщане на метод.

4. Неотзивчиви

В Invokable е свободно обвивка на java.lang.reflect.Method и java.lang.reflect.Constructor . Той предоставя по-опростен API на върха на стандартен API за отражение на Java . Да кажем, че имаме клас, който има два публични метода и единият от тях е окончателен:

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

Сега нека разгледаме somePublicMethod (), използвайки API на Guava и Java 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);

Няма голяма разлика между тези два варианта, но проверката дали методът може да се преодолее е наистина нетривиална задача в Java. За щастие методът isOverridable () от класа Invokable улеснява:

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

Виждаме, че дори такава проста операция се нуждае от много проверки с помощта на стандартен API за отражение . Класът Invokable крие това зад API, който е лесен за използване и много кратък.

5. Заключение

В тази статия разглеждахме API за отразяване на Guava и го сравняваме със стандартната Java. Видяхме как да улавяме родови типове по време на изпълнение и как класът Invokable предоставя елегантен и лесен за използване API за код, който използва отражение.

Изпълнението на всички тези примери и кодови фрагменти може да се намери в проекта GitHub - това е проект на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.