java泛型运行时虽被擦除,但类型信息保留在字节码signature属性中;需从字段声明、父类继承、方法签名或typereference等声明位置反射获取,不可直接从实例反射。

Java 泛型在运行时确实被擦除,list.getClass() 只能得到 ArrayList.class,但真实类型信息并未彻底丢失——它保留在字节码的 Signature 属性中。关键在于:**不能从对象实例反射,而要从声明位置入手**。
从字段声明中获取泛型实参
这是最稳定、最常用的方式。只要字段在类中显式声明了泛型(如 private List<user> users;</user>),就能通过反射提取 User.class:
- 用
clazz.getDeclaredField("users")获取Field对象 - 调用
field.getGenericType()(注意不是getType(),后者已擦除) - 判断是否为
ParameterizedType,是则强转并调用getActualTypeArguments()[0] - 若返回值是
Class>,可直接使用;若是ParameterizedType(如List<string></string>),需递归解析
通过父类继承关系提取类型
适用于抽象基类封装通用逻辑的场景(如 BaseDao<user></user>)。子类必须显式指定泛型(class UserDao extends BaseDao<user></user>):
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 调用
getClass().getGenericSuperclass()(不是getSuperclass()) - 强转为
ParameterizedType,再取getActualTypeArguments()[0] - 该值可能是
Class、WildcardType或TypeVariable,需分别处理 - 遇到
WildcardType(如? extends Product),用getUpperBounds()或getLowerBounds()提取边界
处理方法签名中的嵌套泛型
方法的参数或返回值若含泛型(如 public Map<string list>> getOrders()</string>),其完整签名也保留在字节码中:
- 用
method.getGenericReturnType()或getGenericParameterTypes() - 对每个
Type判断是否为ParameterizedType - 逐层展开:先取
Map的第二个参数(List<order></order>),再从中取Order.class - 若某层是
TypeVariable(如泛型方法<t> T get()</t>),运行时无法还原,需外部约定或传入TypeReference
借助 TypeReference 或匿名子类固化类型
当无法修改继承结构或字段声明,又需动态传入泛型时,可用此技巧(常见于 JSON 反序列化):
-
new TypeReference<list>>() {}</list>或new ArrayList<integer>() {}</integer> - 匿名子类会把父类泛型签名写入自身字节码,可通过
getClass().getGenericSuperclass()提取 - 主流库(Jackson、Gson)已深度适配
TypeReference,语义清晰且安全 - 不建议高频创建匿名子类,有类加载和内存开销
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










