泛型反射获取类型依赖声明位置而非实例,jvm在signature属性保留原始泛型信息;字段和父类继承处可获取,局部变量、new表达式等场景无法获取。

泛型反射获取类型不是靠对象实例,而是靠它在类结构中“声明的位置”。Java 运行时擦除了泛型,但只要泛型写在字段、父类继承、方法签名这些地方,JVM 就会把原始类型信息保留在字节码的 Signature 属性里——反射能读到的,就是这部分。
从字段声明中取泛型实参
这是最常用、最稳的方式。比如你有:
private List<user> users;</user>
不能用 field.getType()(它返回 List.class,已擦除),而要用:
-
field.getGenericType()获取完整类型信息 - 判断是否为
ParameterizedType - 是的话,调用
getActualTypeArguments()得到[User.class]
从父类继承关系中取泛型实参
适用于子类明确指定了父类泛型,例如:
class UserDao extends BaseDao<user> { }</user>
在 UserDao 实例中操作:
- 调用
getClass().getGenericSuperclass()(注意:不是getSuperclass()) - 若返回值是
ParameterizedType,说明父类带具体实参 - 再调用
getActualTypeArguments()[0]就是User.class
处理嵌套和特殊类型
泛型参数本身可能是嵌套的(如 Map<string list>></string>)或非具体类(如 T、? extends Number),需分别识别:
- 遇到
Class类型:直接使用,比如User.class - 遇到
ParameterizedType:递归调用getActualTypeArguments()拆解 - 遇到
TypeVariable(如T):它没有运行时具体类型,需结合上界(getBounds()[0])推测 - 遇到
WildcardType(如? super String):用getLowerBounds()或getUpperBounds()提取边界
哪些地方一定拿不到?
别白费力气尝试以下场景,反射确实无能为力:
- 局部变量:比如
List<string> list = new ArrayList();</string> - new 表达式:比如
new HashMap<string integer>()</string> - 方法调用传参时的泛型:比如
process(new ArrayList<double>())</double> - 未显式指定实参的继承:比如
class RawDao extends BaseDao<t></t>中的T











