Java反射无法直接读取泛型实际类型参数,因编译时已类型擦除;需通过getGenericParameterTypes()或getGenericSuperclass()获取ParameterizedType并解析getActualTypeArguments()来提取实参,且须注意原始类型继承、桥接方法等干扰因素。

Java 反射无法直接读取泛型实际类型参数,因为编译后它们已被擦除 —— 你必须绕过擦除,而不是对抗它。
为什么 Method.getParameterTypes() 返回的是 Object.class 而不是 String.class
这是泛型擦除最直观的表现:方法签名中的类型参数(如 void setName(T name))在字节码中已统一替换为上界类型(默认 Object)。getParameterTypes() 只能返回运行时存在的 Class 对象,而擦除后 T 就是 Object。
- 真正携带泛型信息的是
Method.getGenericParameterTypes(),它返回Type实例(如ParameterizedType),但仅当该方法定义在泛型类/接口中且编译时保留了签名信息 - 若目标方法来自子类对泛型父类的继承(如
class Bar extends Foo<string></string>),则需用Bar.class.getGenericSuperclass()向上追溯原始泛型声明,再解析其中的类型实参 - 注意:匿名内部类、lambda 表达式、桥接方法(bridge method)会进一步干扰
getGenericXXX的结果,建议先用javap -s验证字节码签名
如何从 Bar.class 正确提取出 Foo<string></string> 中的 String
不能依赖 Bar.class.getInterfaces() 或 Bar.class.getSuperclass() 直接拿到带参数的类型 —— 它们返回的是原始类型(Foo.class)。关键路径是获取泛型父类的完整签名:
- 调用
Bar.class.getGenericSuperclass(),得到ParameterizedType实例(前提是Bar显式继承了参数化类型) - 用
((ParameterizedType) type).getActualTypeArguments()提取实参数组,第一个元素即String.class(注意:它可能是Class、ParameterizedType或TypeVariable,需 instanceof 判断) - 若
Bar继承的是原始类型(如class Bar extends Foo),则擦除已发生,getGenericSuperclass()返回的就是Foo.class,此时无解 —— 编译时未指定实参,运行时就不存在
动态注入时怎么避免 ClassCastException?
反射调用 setXxx() 方法本身不会抛异常,但后续从集合或字段读取时可能失败。安全注入的核心是「提前校验」而非「事后强转」:
- 对泛型字段(如
private List<t> items</t>),用Field.getGenericType()解析其ParameterizedType,再逐个检查待注入对象是否匹配getActualTypeArguments()[0] - 对泛型方法参数,优先使用
Method.invoke(obj, args)前做args[i].getClass().isAssignableFrom(expectedType)检查(expectedType来自上一步解析) - 不要依赖
instanceof判断泛型类型(如list instanceof List<string></string>是语法错误),JVM 不支持泛型类型的运行时检查 - 若目标类型是通配符(如
List extends Number>),需用WildcardType接口获取getUpperBounds()并递归校验
最容易被忽略的坑:泛型数组和 new T[] 的替代方案
泛型擦除导致 new T[10] 编译失败,而 (T[]) new Object[10] 在运行时会丢失类型信息,后续向数组写入非预期类型也不会报错 —— 直到从数组读取并强转时才崩。
- 正确做法是传入
Class<T>参数,用Array.newInstance(componentType, length)创建真实类型的数组 - 若泛型类型是嵌套的(如
List<Map<String, Integer>>),Class无法表达,此时必须用TypeToken类(如 Gson 的TypeToken<List<Map<String,Integer>>>(){}.getType())包装完整类型树 - 所有基于
Type的操作都依赖编译器生成的签名信息,所以务必禁用 IDE 的「compact constructor」或 Lombok 的@AllArgsConstructor自动生成(它们可能跳过泛型签名保留)

















