Super Type Tokens in Java Generics – Java Generics中的超级类型标记

最后修改: 2020年 5月 25日

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

1. Overview

1.概述

In this tutorial, we’re going to get familiar with super type tokens and see how they can help us to preserve generic type information at runtime.

在本教程中,我们将熟悉超级类型标记,看看它们如何帮助我们在运行时保留通用类型信息

2. The Erasure

2.抹杀

Sometimes we need to convey particular type information to a method. For example, here we expect from Jackson to convert the JSON byte array to a String:

有时我们需要向一个方法传达特定的类型信息。例如,这里我们希望Jackson将JSON字节数组转换为String:

byte[] data = // fetch json from somewhere
String json = objectMapper.readValue(data, String.class);

We’re communicating this expectation via a literal class token, in this case, the String.class. 

我们通过一个字面的类标记来传达这个期望,在这个例子中,是String.class。

However, we can’t set the same expectation for generic types as easily:

然而,我们不能轻易为通用类型设定同样的期望。

Map<String, String> json = objectMapper.readValue(data, Map<String, String>.class); // won't compile

Java erases generic type information during compilation. Therefore, generic type parameters are merely an artifact of the source code and will be absent at runtime.

Java在编译过程中会抹去通用类型信息。因此,通用类型参数只是源代码的一个工件,在运行时将不存在。

2.1. Reification

2.1.重化

Technically speaking, the generic types are not reified in Java. In programming language’s terminology, when a type is present at runtime, we say that type is reified.

从技术上讲,通用类型在Java中并没有被重化。在编程语言的术语中,当一个类型在运行时存在时,我们说该类型是被重新整合的。

The reified types in Java are as follows:

Java中的重化类型如下。

  • Simple primitive types such as long
  • Non-generic abstractions such as String or Runnable
  • Raw types such as List or HashMap
  • Generic types in which all types are unbounded wildcards such as List<?> or HashMap<?, ?>
  • Arrays of other reified types such as String[], int[], List[], or Map<?, ?>[]

Consequently, we can’t use something like Map<String, String>.class because the Map<String, String> is not a reified type.

因此,我们不能使用像Map<String, String>.class这样的东西,因为Map<String, String>不是一个重化类型。

3. Super Type Token

3.超级类型令牌

As it turns out, we can take advantage of the power of anonymous inner classes in Java to preserve the type information during compile time:

事实证明,我们可以利用Java中匿名内部类的力量,在编译时保留类型信息。

public abstract class TypeReference<T> {

    private final Type type;

    public TypeReference() {
        Type superclass = getClass().getGenericSuperclass();
        type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return type;
    }
}

This class is abstract, so we only can derive subclasses from it.

这个类是抽象的,所以我们只能从它派生出子类。

For example, we can create an anonymous inner:

例如,我们可以创建一个匿名的内部。

TypeReference<Map<String, Integer>> token = new TypeReference<Map<String, String>>() {};

The constructor does the following steps to preserve the type information:

构造函数做了以下步骤来保存类型信息。

  • First, it gets the generic superclass metadata for this particular instance – in this case, the generic superclass is TypeReference<Map<String, Integer>>
  • Then, it gets and stores the actual type parameter for the generic superclass – in this case, it would be Map<String, Integer>

This approach for preserving the generic type information is usually known as super type token:

这种保留通用类型信息的方法通常被称为超级类型标记

TypeReference<Map<String, Integer>> token = new TypeReference<Map<String, Integer>>() {};
Type type = token.getType();

assertEquals("java.util.Map<java.lang.String, java.lang.Integer>", type.getTypeName());

Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
assertEquals("java.lang.String", typeArguments[0].getTypeName());
assertEquals("java.lang.Integer", typeArguments[1].getTypeName());

Using super type tokens, we know that the container type is Map, and also, its type parameters are String and Integer. 

使用超级类型标记,我们知道容器类型是Map,而且,其类型参数是String Integer。

This pattern is so famous that libraries like Jackson and frameworks like Spring have their own implementations of it. Parsing a JSON object into a Map<String, String> can be accomplished by defining that type with a super type token:

这种模式非常有名,以至于Jackson等库和Spring等框架都有自己的实现。将JSON对象解析为Map<String, String> 可以通过用超级类型标记定义该类型来完成。

TypeReference<Map<String, String>> token = new TypeReference<Map<String, String>>() {};
Map<String, String> json = objectMapper.readValue(data, token);

4. Conclusion

4.总结

In this tutorial, we learned how we can use super type tokens to preserve the generic type information at runtime.

在本教程中,我们学习了如何使用超级类型令牌来在运行时保留通用类型信息。

As usual, all the examples are available over on GitHub.

像往常一样,所有的例子都可以在GitHub上找到