TypeToken通过匿名子类将泛型信息固化到字节码中,利用getClass().getGenericSuperclass()获取ParameterizedType,再调用getActualTypeArguments()提取实际类型参数,从而绕过Java泛型擦除限制。

Java 泛型在编译后会被擦除,运行时无法直接获取泛型的实际类型参数(如 List 中的 String)。但通过「类型令牌」(Type Token)——即显式传递 java.lang.reflect.Type 或其子类(如 ParameterizedType)——可以绕过擦除限制,在运行时安全地还原泛型类型信息。
用 ParameterizedType 从匿名子类捕获泛型信息
这是最常用、最可靠的方式:借助匿名内部类保留泛型签名,再通过反射提取。JVM 会将匿名子类的父类泛型信息写入字节码(作为 Signature 属性),因此可被读取。
- 定义一个泛型抽象类或接口(如
TypeReference),并强制用户继承它 - 在构造时调用
getClass().getGenericSuperclass(),强转为ParameterizedType - 从中获取实际类型参数:
getActualTypeArguments()[0]
示例:
public abstract class TypeReference<t> {
private final Type type;
protected TypeReference() {
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof ParameterizedType) {
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
} else {
throw new IllegalArgumentException("TypeReference must be parameterized");
}
}
public Type getType() { return type; }
}
// 使用
TypeReference<list>> ref = new TypeReference<list>>() {};
// ref.getType() 返回 java.util.List<user>(ParameterizedType 实例)
</user></list></list></t>
注意类型变量与通配符的边界处理
并非所有泛型都能无损还原。如果类型参数是类型变量(如 )或含通配符(如 List extends Number>),需检查 Type 的具体实现类:
-
ParameterizedType:对应具体参数化类型(如Map) -
WildcardType:对应通配符(如? super String),可通过getUpperBounds()/getLowerBounds()获取边界 -
TypeVariable:对应声明中的类型变量(如方法中),运行时通常只剩名字和上界,无法还原具体实参T parse(...)
因此,类型令牌只对「在类继承关系中被具体化的泛型」有效,不适用于局部泛型变量或方法泛型参数。
避免常见陷阱:不要依赖 getClass().getTypeParameters()
该方法返回的是声明时的类型变量(如 T),不是运行时传入的实际类型。例如:
public class Box<t> { ... }
Box<string> box = new Box();
box.getClass().getTypeParameters(); // 返回 [T],不是 String
</string></t>
它反映的是类定义,而非实例化时的类型实参,不能用于反推泛型类型。
生产环境建议:优先使用成熟库的类型令牌实现
手动实现易出错,推荐直接使用经过验证的工具:
-
Jackson:用
new TypeReference解析 JSON- >() {}
-
Gson:用
TypeToken.getParameterized(List.class, User.class) -
Guava:提供
TypeToken,支持嵌套泛型、类型转换校验
这些库已处理了数组泛型、多层嵌套、通配符、类型变量等边界情况,比手写更健壮。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











