Руководство по утилизации отражений в Гуаве
1. обзор
В этой статье мы рассмотрим APIGuavareflection, который определенно более универсален по сравнению со стандартным API отражения Java.
Мы будем использоватьGuava для захвата общих типов во время выполнения, а такжеInvokable.
2. Захват универсального типа во время выполнения
In Java, generics are implemented with type erasure. Это означает, что информация об общем типе доступна только во время компиляции, а во время выполнения она больше не доступна.
Например,List<String>, информация об универсальном типе получаетerased at runtime. В связи с этим небезопасно передавать общие объектыClass во время выполнения.
Мы могли бы в конечном итоге назначить два списка, которые имеют разные универсальные типы для одной и той же ссылки, что явно не очень хорошая идея:
List stringList = Lists.newArrayList();
List intList = Lists.newArrayList();
boolean result = stringList.getClass()
.isAssignableFrom(intList.getClass());
assertTrue(result);
Из-за стирания типа методisAssignableFrom() не может знать фактический универсальный тип списков. Он в основном сравнивает два типа, которые представляют собой простоList, без какой-либо информации о фактическом типе.
Используя стандартный API отражения Java, мы можем обнаружить универсальные типы методов и классов. Если у нас есть метод, который возвращаетList<String>, мы можем использовать отражение, чтобы получить тип возвращаемого значения этого метода -ParameterizedType, представляющийList<String>.
Класс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));
Мы получаем реальный тип возвращаемого значения дляFunction, то естьString.. Мы даже можем получить тип записи на карте:
TypeToken
Здесь мы используем метод отраженияgetMethod() из стандартной библиотеки Java для захвата возвращаемого типа метода.
4. Invokableс
Invokable - это плавная оберткаjava.lang.reflect.Method иjava.lang.reflect.Constructor. Он предоставляет более простой API поверх стандартного API Javareflection. Допустим, у нас есть класс с двумя общедоступными методами, один из которых является final:
class CustomClass {
public void somePublicMethod() {}
public final void notOverridablePublicMethod() {}
}
Теперь давайте рассмотримsomePublicMethod(), используя API Guava и стандартный Java APIreflection:
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);
Мы видим, что даже такая простая операция требует множества проверок с использованием стандартного APIreflection. КлассInvokable скрывает это за API, простым в использовании и очень кратким.
5. Заключение
В этой статье мы рассмотрели API отражения Guava и сравнили его со стандартным Java. Мы увидели, как захватывать общие типы во время выполнения и как классInvokable предоставляет элегантный и простой в использовании API для кода, использующего отражение.
Реализация всех этих примеров и фрагментов кода можно найти вGitHub project - это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.