Contents

Guava反射简介

1. 概述

在本文中,我们将研究Guava 反射 API——与标准的 Java 反射 API 相比,它肯定更加通用。

我们将使用Guava在运行时捕获泛型类型,并且我们也将充分利用*Invokable *。

2. 在运行时捕获泛型类型

**在 Java 中,泛型是通过类型擦除实现的。**这意味着泛型类型信息仅在编译时可用,而在运行时 – 它不再可用。

例如,List<String>,关于泛型类型的信息在运行时被删除 。由于这个事实,在运行时传递泛型Class对象是不安全的。

我们最终可能会将两个具有不同泛型类型的列表分配给同一个引用,这显然不是一个好主意:

List<String> stringList = Lists.newArrayList();
List<Integer> intList = Lists.newArrayList();
boolean result = stringList.getClass()
  .isAssignableFrom(intList.getClass());
assertTrue(result);

由于类型擦除,方法isAssignableFrom()无法知道列表的实际泛型类型。它基本上比较了两种类型,它们只是一个List,没有任何关于实际类型的信息。

通过使用标准的 Java 反射 API,我们可以检测方法和类的泛型类型。如果我们有一个返回*List<String>的方法,我们可以使用反射来获取该方法的返回类型——一个表示List<String>*的ParameterizedType

TypeToken类使用此解决方法来允许操作泛型类型。我们可以使用TypeToken类来捕获泛型列表的实际类型,并检查它们是否真的可以被同一个引用引用:

TypeToken<List<String>> stringListToken
  = new TypeToken<List<String>>() {};
TypeToken<List<Integer>> integerListToken
  = new TypeToken<List<Integer>>() {};
TypeToken<List<? extends Number>> numberTypeToken
  = new TypeToken<List<? extends Number>>() {};
assertFalse(stringListToken.isSubtypeOf(integerListToken));
assertFalse(numberTypeToken.isSubtypeOf(integerListToken));
assertTrue(integerListToken.isSubtypeOf(numberTypeToken));

只有integerListToken可以分配给类型为nubmerTypeToken的引用,因为Integer类扩展了Number类。

3. 使用TypeToken捕获复杂类型

假设我们要创建一个泛型参数化类,并且我们希望在运行时获得有关泛型类型的信息。我们可以创建一个具有TypeToken作为字段的类来捕获该信息:

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

然后,当创建该类的实例时,泛型类型将在运行时可用:

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

我们还可以创建具有多个泛型类型的复杂类型的TypeToken,并在运行时检索有关每种类型的信息:

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

我们得到Function的实际返回类型,即String。我们甚至可以在 map 中获取条目的类型:

TypeToken<Map<String, Integer>> mapToken
  = new TypeToken<Map<String, Integer>>() {};
TypeToken<?> entrySetToken = mapToken
  .resolveType(Map.class.getMethod("entrySet")
  .getGenericReturnType());
assertEquals(
  entrySetToken,
  new TypeToken<Set<Map.Entry<String, Integer>>>() {});

这里我们使用 Java 标准库中的反射方法*getMethod()*来捕获方法的返回类型。

4. Invokable

Invokable是java.lang.reflect.Methodjava.lang.reflect.Constructor 的流畅包装器。它在标准 Java 反射 API之上提供了一个更简单的API。假设我们有一个类有两个公共方法,其中一个是最终的:

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

现在让我们使用 Guava API 和 Java 标准反射API 检查somePublicMethod()

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

这两种变体之间没有太大区别,但是检查方法是否可覆盖在 Java 中是一项非常重要的任务。幸运的是,Invokable 类中的*isOverridable()*方法使它更容易:

Method method = CustomClass.class.getMethod("notOverridablePublicMethod");
Invokable<CustomClass, ?> invokable
 = new TypeToken<CustomClass>() {}.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 后面。