Guide to Guava’s Reflection Utilities – Guava’的反思工具指南

最后修改: 2017年 3月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll be looking at the Guava reflection API – which is definitely more versatile compared to the standard Java reflection API.

在这篇文章中,我们将看看Guava反射 API–与标准的Java反射API相比,它绝对是更通用的。

We’ll be using Guava to capture generic types at runtime, and we’ll making good use of Invokable as well.

我们将使用Guava在运行时捕获通用类型,并且我们将很好地利用Invokable以及。

2. Capturing Generic Type at Runtime

2.在运行时捕获通用类型

In Java, generics are implemented with type erasure. That means that the generic type information is only available at compile time and, at runtime – it’s no longer available.

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

For example, List<String>, the information about generic type gets erased at runtime. Due to that fact, it is not safe to pass around generic Class objects at runtime.

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

We might end up assigning two lists that have different generic types to the same reference, which is clearly not a good idea:

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

List<String> stringList = Lists.newArrayList();
List<Integer> intList = Lists.newArrayList();

boolean result = stringList.getClass()
  .isAssignableFrom(intList.getClass());

assertTrue(result);

Because of type erasure, the method isAssignableFrom() can not know the actual generic type of the lists. It basically compares two types that are just a List without any information about the actual type.

由于类型擦除,方法isAssignableFrom() 不能知道列表的实际通用类型。它基本上是在没有任何关于实际类型的信息的情况下比较两个只是一个List的类型。

By using the standard Java reflection API we can detect the generic types of methods and classes. If we have a method that returns a List<String>, we can use reflection to obtain the return type of that method – a ParameterizedType representing List<String>.

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

The TypeToken class uses this workaround to allow the manipulation of generic types. We can use the TypeToken class to capture an actual type of generic list and check if they really can be referenced by the same reference:

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

Only the integerListToken can be assigned to a reference of type nubmerTypeToken because an Integer class extends a Number class.

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

3. Capturing Complex Types Using TypeToken

3.使用TypeToken捕获复杂类型

Let’s say that we want to create a generic parameterized class, and we want to have information about a generic type at runtime. We can create a class that has a TypeToken as a field to capture that information:

假设我们想创建一个通用的参数化类,并且我们想在运行时拥有一个通用类型的信息。我们可以创建一个拥有TypeToken作为字段的类来捕获该信息。

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

Then, when creating an instance of that class, the generic type will be available at runtime:

然后,在创建该类的实例时,通用类型将在运行时可用。

ParametrizedClass<String> parametrizedClass = new ParametrizedClass<String>() {};

assertEquals(parametrizedClass.type, TypeToken.of(String.class));

We can also create a TypeToken of a complex type that has more than one generic type, and retrieve information about each of those types at runtime:

我们还可以创建一个具有多个通用类型的复杂类型的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));

We get an actual return type for Function, that is a String. We can even get a type of the entry in the map:

我们得到Function的实际返回类型,即String。我们甚至可以得到地图中条目的类型。

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>>>() {});

Here we use a reflection method getMethod() from Java standard library to capture the return type of a method.

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

4. Invokable

4.可调用

The Invokable is a fluent wrapper of java.lang.reflect.Method and java.lang.reflect.Constructor. It provides a simpler API on top of a standard Java reflection API. Let’s say that we have a class that has two public methods and one of them is final:

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

class CustomClass {
    public void somePublicMethod() {}

    public final void notOverridablePublicMethod() {}
}

Now let’s examine the somePublicMethod() using Guava API and Java standard reflection API:

现在让我们使用Guava API和Java标准reflection 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);

There is not much difference between these two variants, but checking if a method is overridable is a really non-trivial task in Java. Fortunately, the isOverridable() method from the Invokable class makes it easier:

这两种变体之间没有太大的区别,但是在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);

We see that even such a simple operation needs a lot of checks using standard reflection API. The Invokable class hides this behind the API that is simple to use and very concise.

我们看到,即使是这样一个简单的操作也需要使用标准的反射 API进行大量的检查。Invokable类将这些隐藏在API后面,使用起来很简单,而且非常简洁。

5. Conclusion

5.结论

In this article, we were looking at the Guava reflection API and compare it to the standard Java. We saw how to capture generic types at runtime, and how the Invokable class provides elegant and easy to use API for code that is using reflection.

在这篇文章中,我们看了Guava的反射API,并将其与标准Java进行了比较。我们看到了如何在运行时捕获通用类型,以及Invokable 类如何为使用反射的代码提供优雅且易于使用的API。

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub项目中找到–这是一个Maven项目,所以应该很容易导入并按原样运行。