java泛型是编译期特性,运行时类型擦除,但可通过子类显式继承(如child extends parent)、typereference匿名子类固化、父类提供type token三种方式间接获取泛型实参。

Java 中泛型是**编译期特性**,运行时会发生类型擦除,所以子类继承父类时,无法直接在运行时获取父类的泛型实际类型(比如 Parent<string></string> 擦除后只剩 Parent)。但有几种常用方式可以在子类中“保留”或**间接获取**父类泛型实参,核心思路是利用编译期可推断的类型信息 + 运行时反射辅助。
通过子类显式声明泛型并传递类型参数
让子类自己带上泛型,并在继承时明确指定父类泛型实参,这样编译器能推导出类型,部分场景下可通过反射提取(需配合 Type 层次结构):
- 父类定义为
class Parent<t> { ... }</t> - 子类写成
class Child extends Parent<string> { ... }</string>—— 此时Child的直接父类类型在字节码中记录为Parent<string></string>,尚未被擦除 - 在
Child中可用getClass().getGenericSuperclass()获取ParameterizedType,再调用getActualTypeArguments()取出String.class
使用 TypeReference 或匿名子类“固化”泛型信息
这是最常用、最可靠的技巧:借助匿名内部类在创建时捕获泛型签名,绕过直接实例化的擦除问题:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 定义一个空的抽象基类如
abstract class TypeReference<t> {}</t> - 在子类中写:
TypeReference<list>> ref = new TypeReference<list>>() {};</list></list> - 由于匿名子类会保留其父类的泛型实参(
TypeReference<list>></list>),调用ref.getClass().getGenericSuperclass()就能得到完整的带参类型 - 很多 JSON 库(如 Jackson、Gson)的
TypeReference就是基于这个原理
在父类中提供类型令牌(Type Token)字段
如果控制父类设计,可在父类中强制子类传入 Class<t></t> 或 Type,把类型信息“存下来”:
- 父类加构造函数:
protected Parent(Class<t> type) { this.type = type; }</t> - 子类构造时显式传入:
super(String.class); - 后续所有类型相关逻辑(如反序列化、类型检查)都基于该
Class对象,不依赖反射推断 - 缺点是不够自动,需要子类配合;优点是简单、稳定、100% 运行时可用
注意边界:无法恢复已擦除的局部泛型或通配符
以下情况**无法还原**原始泛型实参:
- 父类用
Parent>或Parent extends Number>继承 —— 通配符信息在字节码中不完整,反射拿不到具体上界 - 泛型参数来自方法签名或局部变量(如
<t> void foo(T t)</t>)—— 方法级泛型完全不参与类签名,运行时不可见 - 子类未显式指定父类泛型(如
class Child extends Parent)—— 父类退化为原生类型,无泛型信息可查
不复杂但容易忽略:关键在于“谁在编译时绑定了具体类型”,只有在类继承关系中显式写出泛型实参,且该子类被 JVM 加载时,其 Class 对象才可能携带可反射提取的泛型信息。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










