Java泛型虽经类型擦除,但字段、方法签名及父类/接口声明中的泛型信息仍保留在字节码中,可通过反射获取;局部变量和未具体化的类型变量(如T)则不可见。

Java 泛型在编译后确实会被擦除(type erasure),但**并非所有泛型信息都完全丢失**。通过反射,我们仍能在某些场景下获取到真实的泛型类型,关键在于:**泛型信息是否在字节码中被保留**,以及**你访问的是哪个结构(字段、方法返回值、方法参数、父类/接口声明)**。
哪些地方能拿到真实泛型类型?
Java 会将以下位置的泛型信息保留在 class 文件中(供反射读取):
-
字段声明中的泛型类型(如
private List)→ 可用names; Field.getGenericType() -
方法返回类型和参数类型中的泛型(如
public Map)→ 用getData() {...} Method.getGenericReturnType()或getGenericParameterTypes() -
父类或实现接口的泛型实参(如
class MyList extends ArrayList)→ 用{ ... } Class.getGenericSuperclass()或getGenericInterfaces()
⚠️ 注意:local variables(局部变量)、method body 内部的泛型、以及 泛型类型变量本身(如 T)未被具体化时,这些信息在运行时不可见。
如何正确解析 ParameterizedType
当你调用 getGenericType() 得到的通常是 ParameterizedType(表示带实参的泛型类型,如 List),需安全强转并提取:
Field field = clazz.getDeclaredField("items");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type rawType = pt.getRawType(); // → List.class
Type[] actualTypeArgs = pt.getActualTypeArguments(); // → [String.class]
if (actualTypeArgs.length > 0) {
Type arg = actualTypeArgs[0];
if (arg instanceof Class) {
Class> clazzArg = (Class>) arg; // 如 String.class
}
}
}
获取父类泛型实参(最常用技巧)
这是框架(如 Gson、Jackson、MyBatis)推断泛型类型的经典方式,适用于继承泛型基类的子类:
public abstract class BaseDao<t> {
private Class<t> entityClass;
@SuppressWarnings("unchecked")
public BaseDao() {
// 获取当前类(子类)继承 BaseDao 时传入的 T 实际类型
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof ParameterizedType) {
Type[] args = ((ParameterizedType) superClass).getActualTypeArguments();
if (args.length > 0 && args[0] instanceof Class) {
this.entityClass = (Class<t>) args[0];
}
}
}
}
// 使用示例
public class UserDao extends BaseDao<user> { } // 运行时可拿到 User.class</user></t></t></t>
✅ 关键点:子类继承时写死的泛型实参()会被编译器记录在子类的 Signature 属性中,反射可读。
注意 Type 的多种实现类型
Type 是个接口,实际可能是:
-
Class:原始类型或无泛型的类(如String.class) -
ParameterizedType:带实参的泛型(如List) -
TypeVariable:类型变量(如T、K),此时无法知道具体类型 —— 擦除已发生 -
WildcardType:通配符(如? extends Number) -
GenericArrayType:泛型数组(如T[]或List)[]
务必做类型判断再处理,避免 ClassCastException。
不复杂但容易忽略:泛型反射不是“恢复擦除”,而是读取编译期写入字节码的元数据。只要你在字段、方法签名或继承关系中明确写出了具体类型(而非仅用 T),它就大概率可查。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











