java泛型在运行时被擦除,反射无法直接获取实际类型;需通过匿名子类typetoken、field/method的getgenerictype等保留结构间接获取,但无法还原完整类型约束。

Java反射拿不到泛型实际类型?因为Type擦除是编译期行为
Java泛型在运行时被擦除,getClass() 返回的永远是原始类型(如 ArrayList 而不是 ArrayList<string></string>)。这不是反射API的缺陷,而是JVM规范决定的——类型信息根本没进字节码。想绕过它,得靠编译期还能保留的结构:带泛型的类、方法签名、字段声明。
用TypeToken保存泛型信息:为什么必须继承匿名子类
TypeToken 本身不神奇,它的核心技巧是利用匿名内部类的 getGenericSuperclass() 获取父类声明中的完整 Type。只有继承时写死泛型参数,JVM才会在类字节码的 Signature 属性里存下这个信息。
- ✅ 正确写法:
new TypeToken<list>>() {}</list>—— 匿名子类,泛型被固化 - ❌ 错误写法:
new TypeToken(someType)—— 构造函数传参,类型早已擦除 - ⚠️ 注意:
TypeToken是 Guava 提供的,不是 JDK 原生 API;若不用 Guava,得自己封装类似逻辑
从Field或Method中提取泛型类型:别只调用getGenericType()
Field.getGenericType() 和 Method.getGenericReturnType() 确实返回 Type,但直接 toString() 很可能还是看到 List<t></t> 这种变量名。关键是要做类型解析:
- 遇到
ParameterizedType:用getActualTypeArguments()拿真实类型数组(比如List<map integer>></map>的第二层泛型) - 遇到
TypeVariable:说明是类/方法定义里的形参(如<t extends number></t>),此时需结合所在类的getTypeParameters()和实际上下文推导 - 遇到
WildcardType:如? super CharSequence,需分别检查getLowerBounds()和getUpperBounds()
常见坑:ClassCastException 和泛型数组创建失败
拿到 Type 不等于能直接 new 实例。JVM 不允许泛型数组(new List<string>[10]</string> 编译报错),而用 Array.newInstance() 传入 type.getClass() 又会得到原始类型数组。
立即学习“Java免费学习笔记(深入)”;
- 泛型数组只能靠
Object[]+ 显式转型,且无法在运行时校验元素类型 - 用
TypeToken解析出List<string></string>后,仍不能直接 cast 到List<string></string>—— 反射返回的 list 实际是ArrayList,类型安全靠开发者保证 - 某些场景(如 JSON 反序列化)依赖
Type构造器传参,但 Gson/Jackson 对嵌套通配符支持有限,容易静默退化为LinkedTreeMap
泛型类型信息就像一张快照,拍下来容易,还原成可执行的类型约束很难。最稳的路,是让泛型出现在类声明或字段上,而不是临时拼出来的 Type。










