【发布时间】:2015-05-02 17:06:58
【问题描述】:
我知道在 Java 中与 C# 泛型相反,例如,C# 泛型是编译时特性,并且通过类型擦除被删除。那么,Gson 的TypeToken 究竟是如何工作的呢?它如何获取对象的泛型类型?
【问题讨论】:
标签: java generics gson erasure
我知道在 Java 中与 C# 泛型相反,例如,C# 泛型是编译时特性,并且通过类型擦除被删除。那么,Gson 的TypeToken 究竟是如何工作的呢?它如何获取对象的泛型类型?
【问题讨论】:
标签: java generics gson erasure
From §4.6 of the JLS(强调我的):
类型擦除是从类型(可能包括参数化类型和类型变量)到类型(绝不是参数化类型或类型变量)的映射。我们写 |T|用于类型 T 的擦除。擦除映射定义如下:
参数化类型(第 4.5 节)G 的擦除是 |G|。
嵌套类型 T.C 的擦除是 |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。
【讨论】:
TypeReference<Foo>() {}的擦除是TypeReference<Foo>() {}。这意味着两个
class MyAwesomeList<T> extends java.util.ArrayList<T> { }new MyAwesomeList<String>().getClass().getGenericSuperClass()
The erasure of every other type is the type itself.在这里发挥作用
Java 的类型擦除适用于单个对象,而不是类、字段或方法。 TypeToken 使用匿名类来确保它保留泛型类型信息,而不仅仅是创建一个对象。
【讨论】:
Gson TypeToken 使用 Neal Gafter 的 super type tokens 模式。此模式基于 docs 中的 Class#getGenericSuperclass() 方法
返回代表实体直接超类的
Type(类、接口、原始类型或 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)。
【讨论】: