Java泛型运行时虽被擦除,但通过反射可获取真实类型参数:从父类继承关系(getGenericSuperclass)、字段声明(getGenericType)、方法签名(getGenericParameterTypes等)三处入口解析ParameterizedType,递归提取实参;局部变量、new表达式及直接实例化基类无法恢复泛型信息。

Java 泛型在运行时确实被擦除,但只要泛型信息以结构化方式写入字节码(比如出现在继承关系、字段声明、方法签名中),就能通过反射拿到真实类型参数。关键不是“从对象实例出发”,而是“找对反射入口”。
从父类继承关系中提取实参
这是最稳定的方式,适用于子类明确指定了泛型父类实参的场景。
- 调用 getClass().getGenericSuperclass() 获取带泛型的父类类型
- 判断返回值是否为 ParameterizedType,是则强转
- 调用 getActualTypeArguments()[0] 获取第一个泛型实参(如 User.class)
- 若实参本身是 ParameterizedType(如 List<String>),需递归解析其 getActualTypeArguments()
从字段声明中读取泛型类型
字段若在类中显式声明了泛型(如 private Map<String, List<Order>> data;),泛型信息可完整保留。
- 用 Field.getGenericType() 替代 Field.getType()(后者只返回原始类型)
- 若返回值是 ParameterizedType,调用 getRawType() 得到原始类(如 Map.class)
- 再调用 getActualTypeArguments() 拆解各层泛型(String.class 和 List<Order>)
- 遇到嵌套类型(如 List<Order>),继续判断其是否为 ParameterizedType 并递归处理
从方法参数或返回值中获取
方法签名中的泛型不会被擦除,比如 public List<User> findAll() 或 <T> T parse(String json)。
立即学习“Java免费学习笔记(深入)”;
- 用 Method.getGenericParameterTypes() 或 getGenericReturnType()
- 对每个 Type 判断类型:是 ParameterizedType 就拆解;是 TypeVariable(如 T)就查 getBounds() 看上界;是 WildcardType(如 ? extends Number)就用 getUpperBounds()
- 静态泛型方法(如 <T> T get())中的 T 在运行时无具体类型,反射只能返回 TypeVariable,无法还原
注意事项与常见失效场景
不是所有泛型都能反射出来,以下情况无法恢复:
- 局部变量:如 List<String> list = new ArrayList<>(); —— 字节码不记录该泛型
- new 表达式:如 new HashMap<String, Integer>() —— 运行时只剩原始类型
- 直接实例化泛型基类:如 new Generic<String>(),其 getGenericSuperclass() 返回 Object.class,不是 ParameterizedType
- 要让基类泛型可用,必须用 匿名子类:如 new Generic<String>() {},编译器会把泛型信息写入子类 Signature


















