
泛型方法反射调用时 getGenericReturnType 返回 Object 而不是实际类型
Java 类型擦除会让编译后的字节码里泛型信息基本消失,但方法签名中的泛型参数(比如 List<string></string>)会保留在 Method 的元数据中——前提是它出现在方法声明里,而不是局部变量或返回值是泛型擦除后的原始类型。
常见错误现象:你写了个 public <t> List<t> getData()</t></t>,用反射调用后想通过 method.getGenericReturnType() 拿到 List<string></string>,结果得到的是 List> 或直接 List,甚至强转失败。
-
getGenericReturnType()只能拿到方法声明时的「类型变量」或「带参类型」,不会还原运行时传入的具体类型(比如getData<string>()</string>中的String) - 如果方法是静态泛型、或者泛型参数来自类层级(如
class Foo<t> { T get(); }</t>),必须结合getDeclaringClass().getTypeParameters()和getGenericReturnType()做类型变量映射 - 若目标方法返回的是泛型通配符(如
List extends Number>),getGenericReturnType()返回的是ParameterizedType,需手动遍历getActualTypeArguments()解析
反射获取泛型字段类型时 getGenericType() 返回 Class 而非 ParameterizedType
字段泛型信息在字节码中保留得比局部变量好,但仍有陷阱:只有字段声明本身含泛型(如 private List<integer> items;</integer>)才能正确读出;如果字段是原始类型(List items;)或被子类覆写过,泛型就丢了。
使用场景:你想用反射自动把 JSON 字符串反序列化成字段对应的具体泛型类型(比如 Map<string user></string>),但发现 Jackson/Gson 无法推断 User。
立即学习“Java免费学习笔记(深入)”;
- 检查字段是否真有泛型签名:用
field.getGenericType() instanceof ParameterizedType判断,否则就是擦除了 - 若字段属于泛型类(如
class Repo<t> { T data; }</t>),要先从宿主实例的getClass().getGenericSuperclass()拿到实际类型参数,再替换字段声明里的T - 注意内部类字段:匿名类、Lambda 表达式捕获的泛型字段,JVM 不保证保留泛型签名,实测常返回
Class而非ParameterizedType
Class>.getDeclaredMethods() 找不到带泛型参数的重载方法
泛型方法在字节码中会被“桥接方法”(bridge method)覆盖。比如你写了 <t extends charsequence> void accept(T t)</t>,编译器会生成一个桥接方法 void accept(CharSequence t),而原始泛型方法本身可能不对外暴露。
错误现象:反射调用时报 NoSuchMethodException,明明方法存在;或者找到两个同名方法,但参数类型看起来一样(都是 Object)。
- 用
method.isBridge()过滤掉桥接方法,只处理!method.isBridge() && !method.isSynthetic()的方法 - 泛型方法的参数类型必须用
method.getGenericParameterTypes()获取,getParameterTypes()返回的全是Object.class或原始类型 - 若方法参数是泛型数组(如
T[]),getGenericParameterTypes()返回的是GenericArrayType,需调用getGenericComponentType()才能得到T
Gson/TypeToken 在反射中绕过类型擦除的局限性
TypeToken 是常用补偿手段,但它本质是靠创建匿名子类来捕获泛型信息(利用 getClass().getGenericSuperclass())。这个技巧在反射上下文中极易失效。
性能影响:每次 new TypeToken>() 都会触发类加载,频繁使用会堆积临时类,尤其在 OSGi 或热部署环境容易引发
OutOfMemoryError: Metaspace。
- 不能对局部变量、方法返回值、lambda 参数用
TypeToken—— 匿名子类只能捕获类/字段/方法签名层级的泛型 - 若泛型嵌套过深(如
Map<string list>>></string>),TypeToken解析可能丢掉中间某层类型参数,建议拆成多级TypeToken分别构造 - Android 上 Dalvik/ART 对泛型签名支持不一致,某些版本中
TypeToken读出的ParameterizedType的getOwnerType()为null,导致类型替换失败
最麻烦的地方不在怎么补,而在补完之后没人校验——比如你靠 TypeToken 构造了 List<string></string>,但实际 JSON 是 [1,2,3],Gson 默认静默转成 ["1","2","3"],类型安全全靠开发者自己加运行时校验。









