28

我知道在 Java 中与之相反,例如,C# 泛型是编译时特性,并且通过类型擦除被删除。那么,Gson'sTypeToken究竟是如何工作的呢?它如何获得对象的泛型类型?

4

3 回答 3

38

这是一个把戏!

来自 JLS 的§4.6(强调我的):

类型擦除是从类型(可能包括参数化类型和类型变量)到类型(从不参数化类型或类型变量)的映射。我们写 |T| 用于类型 T 的擦除。擦除映射定义如下:

参数化类型(§4.5)G的擦除是|G|。

嵌套类型 TC 的擦除是 |T|.C。

数组类型 T[] 的擦除是 |T|[]。

类型变量的擦除(第 4.4 节)是其最左边界的擦除。

所有其他类型的擦除都是类型本身。

因此,如果您声明一个具有其自身匿名子类的类,它将保持其参数化类型;它没有被删除。因此,请考虑以下代码:

import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.HashMap;

public class Erasure<T>
{
    public static void main(String...strings) {
      Class<?> foo = new Erasure<HashMap<Integer, String>>() {}.getClass();
      ParameterizedType t = (ParameterizedType) foo.getGenericSuperclass();
      System.out.println(t.getOwnerType());
      System.out.println(t.getRawType());
      System.out.println(Arrays.toString(t.getActualTypeArguments()));
    }
}

这输出:

null
class Erasure
[java.util.HashMap<java.lang.Integer, java.lang.String>]

ClassCastException请注意,如果由于擦除而没有匿名声明该类,您将得到一个;超类不是参数化类型,而是Object.

于 2015-05-02T20:17:50.737 回答
11

Java 的类型擦除适用于单个对象,而不是类、字段或方法。TypeToken 使用匿名类来确保它保留通用类型信息,而不仅仅是创建一个对象。

于 2015-05-02T20:09:02.997 回答
1

Gson TypeToken 使用 Neal Gafter 的超级类型令牌模式。这种模式基于Class#getGenericSuperclass()方法,来自 docs

返回Type此 Class 表示的实体(类、接口、原始类型或 void)的表示直接超类。 如果超类是参数化类型,则Type返回的对象必须准确反映源代码中使用的实际类型参数。

这实质上意味着如果您有一个扩展参数化类的类,那么您可以将实际类型参数传递给超类

((ParameterizedType)myClassObj.getGenericSuperClass()).getActualTypeArguments()

因此,当您在 Gson 中创建类型标记时

Type someTypeToken = new TypeToken<Collection<Integer>>(){};

// This essentially is same as defining
class AnonClass extends TypeToken<Collection<Integer>>{}

现在您可以轻松地将类型参数获取到上面指定的超类型(TypeToken)。

于 2021-08-06T05:55:56.637 回答