类型令牌是解决Java泛型运行时类型丢失的关键方案:简单类型用Class<T>,复杂泛型则通过匿名子类(如new TypeReference<List<User>>(){})利用字节码中保留的泛型签名来固化完整类型结构。

Java 泛型在编译后会经历类型擦除,所以 List<String> 和 List<Integer> 在运行时都是 List,JVM 无法直接获取泛型参数的实际类型。但很多时候(比如 JSON 反序列化、依赖注入、集合类型推断),我们确实需要在运行时知道具体的泛型类型。这时,**类型令牌(Type Token)** 就是关键解决方案——它通过显式传入 Class 或更通用的 Type 对象来“携带”类型信息。
为什么 Class<T> 本身不能完全解决泛型类型保留问题
Class<List<String>> 这样的写法在 Java 中是非法的:因为 List<String> 是参数化类型(ParameterizedType),而 Class 只能表示原始类型(如 List.class)或具体类(如 String.class),无法直接表示带泛型参数的类型。所以仅靠 Class 只能保留**顶层的原始类型**,无法保留嵌套的泛型结构(如 List<Map<String, Integer>>)。
用 Class<?> 做简单类型令牌(适用于无泛型参数的场景)
当目标类型本身不含泛型参数(如 String、User、Integer),直接传入 Class<T> 是最常用、最轻量的方式:
- 方法签名示例:
<T> T fromJson(String json, Class<T> type) - 调用方式:
gson.fromJson(json, User.class)—— 此时User.class就是类型令牌,告诉 Gson “反序列化成 User 类的对象” - 优点:简洁、类型安全、无需额外类
- 局限:无法表达
List<User>或Map<String, User>这类带泛型的类型
用 TypeReference(或自定义 TypeToken)保留完整泛型结构
要保留带泛型的类型信息(如 List<User>),需借助 Java 的 java.lang.reflect.Type 体系,并利用**匿名子类的泛型签名在字节码中被保留**这一特性:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 常见做法是继承一个泛型抽象类(如 Jackson 的
TypeReference<T>或 Gson 的TypeToken<T>),在匿名子类中写出完整泛型类型 - 例如:
new TypeReference<List<User>>() {}—— 编译后,JVM 会在该匿名类的getGenericSuperclass()中保留List<User>的完整ParameterizedType信息 - 框架内部通过反射提取这个泛型父类,再递归解析实际类型参数(
User.class) - 本质是“用子类的泛型声明作为类型证据”,绕过泛型擦除
手动构建 ParameterizedType(高级场景,慎用)
如果无法使用框架提供的 TypeReference,也可手动构造 ParameterizedType 实例(需配合 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 或使用 java.lang.reflect.TypeVariable 等,但注意:标准 API 不提供公有构造器):
- 推荐方式是借助
java.lang.reflect.Type的实现类(如第三方库guava提供的Types.newParameterizedType()) - 示例:
Types.newParameterizedType(List.class, User.class)返回一个可被 Gson/Jackson 识别的ParameterizedType - 不建议手写反射代码去 new sun.* 类,兼容性和稳定性差
类型令牌不是语法糖,而是对泛型擦除限制的一种务实补偿。核心思路就两条:对简单类型用 Class,对复杂泛型用“匿名子类 + 泛型签名”来固化类型结构。只要理解了字节码里泛型签名的保留机制,就很容易看懂主流框架的 TypeToken 实现逻辑。

















