背景
Java的泛型是在编译阶段实现的,这意味着泛型类型信息在编译后的字节码中通常是不可用的,因为它们会被擦除(Type Erasure)。例如,List<String>
和List<Integer>
在运行时都被视为简单的List
。
泛型类型的保留
然而,当你创建一个匿名内部类,并且这个匿名内部类继承自一个具体的泛型类型时,Java编译器会将这个具体的泛型类型作为匿名内部类的一部分类型信息保留下来。这是因为匿名内部类是一个特殊的类,它没有名称,但它扩展了一个父类或实现了一个接口,并且这个信息(包括泛型类型参数)是必须的,以便于编译器能够正确地生成字节码。
示例解释
假设我们有一个泛型接口Interface<T>
:
java
public interface Interface<T> {
T performAction();
}
当我们创建一个匿名内部类并为其指定一个具体的泛型类型String
时,如下:
java
Interface<String> myInterface = new Interface<String>() {
@Override
public String performAction() {
return "Action Performed";
}
};
在上面的代码中,我们创建了一个实现了Interface<String>
的匿名内部类。这里的关键点是,尽管泛型类型参数String
通常会在运行时被擦除,但由于这是一个匿名内部类的创建,Java编译器实际上会将String
类型参数作为这个匿名内部类的一部分保留下来。
如何获取这个类型信息
TypeToken
类的作用就是利用这个特性来捕获这个保留下来的类型信息。当我们使用TypeToken
时,我们实际上是在创建一个匿名子类,并让它继承自带有泛型类型参数的TypeToken
类。这样,我们就可以通过反射来获取这个类型信息:
java
TypeToken<Interface<String>> typeToken = new TypeToken<Interface<String>>() {};
Type type = typeToken.getType(); // 这个type可以用来获取完整的泛型类型信息
在上面的代码中,new TypeToken<Interface<String>>() {}
创建了一个匿名子类,它继承自TypeToken
并且指定了泛型参数为Interface<String>
。通过调用getType()
方法,我们可以获得这个泛型接口的完整类型信息,包括它的泛型类型参数String
。
这个Type
对象可以用于反序列化操作,比如在Gson中,我们可以使用这个Type
对象来告诉Gson如何正确地将JSON数据转换为特定的泛型类型的Java对象。
希望这个解释和示例能够帮助你更好地理解TypeToken
的工作原理。