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

Java 泛型在编译后会经历类型擦除,所以 List<string></string> 和 List<integer></integer> 在运行时都是 List,JVM 无法直接获取泛型参数的实际类型。但很多时候(比如 JSON 反序列化、依赖注入、集合类型推断),我们确实需要在运行时知道具体的泛型类型。这时,**类型令牌(Type Token)** 就是关键解决方案——它通过显式传入 Class 或更通用的 Type 对象来“携带”类型信息。
为什么 Class 本身不能完全解决泛型类型保留问题
Class<list>></list> 这样的写法在 Java 中是非法的:因为 List<string></string> 是参数化类型(ParameterizedType),而 Class 只能表示原始类型(如 List.class)或具体类(如 String.class),无法直接表示带泛型参数的类型。所以仅靠 Class 只能保留**顶层的原始类型**,无法保留嵌套的泛型结构(如 List<map integer>></map>)。
用 Class> 做简单类型令牌(适用于无泛型参数的场景)
当目标类型本身不含泛型参数(如 String、User、Integer),直接传入 Class<t></t> 是最常用、最轻量的方式:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 方法签名示例:
<t> T fromJson(String json, Class<t> type)</t></t> - 调用方式:
gson.fromJson(json, User.class)—— 此时User.class就是类型令牌,告诉 Gson “反序列化成 User 类的对象” - 优点:简洁、类型安全、无需额外类
- 局限:无法表达
List<user></user>或Map<string user></string>这类带泛型的类型
用 TypeReference(或自定义 TypeToken)保留完整泛型结构
要保留带泛型的类型信息(如 List<user></user>),需借助 Java 的 java.lang.reflect.Type 体系,并利用**匿名子类的泛型签名在字节码中被保留**这一特性:
- 常见做法是继承一个泛型抽象类(如 Jackson 的
TypeReference<t></t>或 Gson 的TypeToken<t></t>),在匿名子类中写出完整泛型类型 - 例如:
new TypeReference<list>>() {}</list>—— 编译后,JVM 会在该匿名类的getGenericSuperclass()中保留List<user></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 实现逻辑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










