java泛型虽在编译期被擦除,但通过parameterizedtype结合父类泛型声明、字段类型、匿名内部类或typereference等方式,仍可在运行时获取真实泛型参数。

Java 泛型在编译期会被擦除,运行时 list.getClass() 只能拿到 ArrayList.class,无法直接得知它存的是 String 还是 User。但通过合理利用 ParameterizedType 和类型声明上下文(如父类、字段、方法返回值),仍可获取真实泛型参数。
利用父类的泛型声明(最常用)
当一个类继承自带泛型的父类(如 BaseDao<user></user>),可通过 getGenericSuperclass() 获取其带参类型:
- 确保子类显式继承具体化泛型,例如
class UserDao extends BaseDao<user> {}</user>,而非extends BaseDao>或裸类型 - 在子类中调用
((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0] - 返回的是
Type,若为Class类型可强转,若含嵌套(如List<string></string>)需进一步解析
通过字段类型获取泛型信息
类中声明的泛型字段(如 private List<order> orders;</order>)可在运行时提取实际类型:
- 用
getClass().getDeclaredField("orders").getGenericType()得到ParameterizedType - 注意需先
setAccessible(true),且字段不能是局部变量或参数——反射只作用于成员字段 - 若字段类型是通配符(如
List extends Product>),getActualTypeArguments()返回的是WildcardType,需调用getUpperBounds()或getLowerBounds()提取边界类型
借助匿名内部类“固化”类型(临时方案)
在需要动态传入泛型类型又无法修改继承结构时,可用匿名子类技巧绕过擦除:
-
new ArrayList<string>() {}</string>创建的是匿名子类,其父类就是ArrayList<string></string>,泛型信息被保留在getGenericSuperclass()中 - 适用于工具方法中临时获取类型,例如
getType(new ArrayList<integer>() {})</integer> - 缺点是每次调用都生成新类,有类加载和内存开销,不建议高频或框架底层使用
使用 TypeReference(Jackson/Gson 等库推荐方式)
像 Jackson 的 TypeReference<list>> {}</list> 本质也是匿名子类方案,但封装得更安全易用:
- 继承
TypeReference并写空实现体,构造时即捕获泛型签名 - 内部通过
getClass().getGenericSuperclass()提取并缓存,避免重复解析 - 比手写匿名类更语义清晰,且主流 JSON 库已深度适配,推荐在序列化/反序列化场景优先采用
泛型擦除不可逆,但类型信息从未真正丢失——它存在于字节码的签名属性中。关键在于选择正确的反射入口:继承关系、字段声明、方法签名或手动构造类型引用。选对上下文,再配合 ParameterizedType 和 WildcardType 的正确解析,就能稳定拿到真实类型。









