Java泛型运行时虽被擦除,但字段、父类、方法签名等结构化位置的泛型信息保留在字节码中,可通过反射获取;最稳定方式是从字段声明(如private List<User> users)提取ParameterizedType并调用getActualTypeArguments()[0]得User.class。

Java 泛型在运行时确实被擦除,但只要泛型信息以结构化方式写入字节码(比如字段声明、父类继承、方法签名),就能通过反射拿到真实类型——不是“恢复擦除”,而是读取编译器保留下来的泛型签名。
从字段声明中提取泛型实参
这是最稳定、最常用的方式。前提是字段直接声明为参数化类型,例如:private List<User> users;
- 用
clazz.getDeclaredField("users")获取Field对象 - 调用
field.getGenericType()—— 返回的是Type接口,不是Class - 判断是否为
ParameterizedType,是则强转 - 调用
getActualTypeArguments()[0]得到User.class;如果返回的是嵌套的ParameterizedType(如Map<String, Integer>),需递归拆解
从父类继承关系中获取泛型实参
当子类显式指定父类泛型时,JVM 会把该信息写入字节码。例如:class UserList extends ArrayList<User> { }
- 调用
getClass().getGenericSuperclass() - 检查返回值是否为
ParameterizedType - 强转后调用
getActualTypeArguments()[0]即得User.class - 注意:
getSuperclass()返回的是擦除后的原始类(如ArrayList.class),必须用getGenericSuperclass()
从方法参数或返回值中解析泛型
方法签名中的泛型不会被擦除。例如:public Map<String, List<Order>> getOrdersMap() { } 或 void process(List<User> list)
立即学习“Java免费学习笔记(深入)”;
- 用
clazz.getDeclaredMethod("getOrdersMap")获取Method对象 - 调用
method.getGenericReturnType()或method.getGenericParameterTypes() - 对每个
Type判断是否为ParameterizedType,再调用getRawType()和getActualTypeArguments() - 嵌套泛型(如
List<Map<K, V>>)需要逐层递归处理
处理特殊类型与常见限制
泛型参数不总是 Class,还可能是:
-
TypeVariable:如private List<T> data;,此时getActualTypeArguments()[0]是TypeVariable,需结合宿主类的泛型上下文向上查找(如通过getGenericSuperclass()) -
WildcardType:如? extends Number,可用getUpperBounds()[0]获取上界 - 局部变量、new 表达式、裸类型、泛型方法调用实参 等场景无法获取真实泛型类型,因为字节码中没有对应签名


















