Java泛型类型擦除是设计前提而非障碍,需接受运行时无泛型信息的事实;编译器按边界规则自动擦除,泛型方法需Class参数补位,重载、数组和异常类须规避擦除陷阱,反射获取泛型信息可用TypeToken技巧。

Java 中泛型类型擦除不是障碍,而是编写通用代码的前提条件——关键在于接受“运行时无泛型信息”这一事实,并围绕它设计方法和类。
泛型类的编写:按边界规则擦除,靠编译器补转换
定义泛型类时,不用手动处理擦除,编译器会自动按规则转换。你只需关注约束是否合理:
- 无上界(<T>):字段、参数、返回值统一视为 Object,编译器在 get 处插入
checkcast指令保证安全; - 单上界(<T extends Number>):所有 T 的位置被擦成 Number,避免向下转型风险;
- 多上界(<T extends Comparable & Cloneable>):只保留第一个接口 Comparable 作为擦除目标,Cloneable 仅参与编译期校验。
示例:一个安全的数值容器
public class NumberBox<T extends Number> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; } // 返回值擦为 Number,但调用方仍能接收 Integer/Double
}
使用时无需强转,也不会因擦除而丢失语义:IntegerBox 和 DoubleBox 共享同一份字节码,但类型约束由编译器全程把关。
立即学习“Java免费学习笔记(深入)”;
泛型方法的编写:不依赖运行时类型,用 Class 补位
泛型方法无法在运行时 new T() 或 instanceof T[],所以需显式传入类型凭证:
- 需要创建实例时,加一个 Class<T> type 参数,再用
type.getDeclaredConstructor().newInstance(); - 需要做类型判断时,用
obj.getClass() == type替代obj instanceof T; - 需要数组时,写成
(T[]) new Object[size]并抑制 unchecked 警告,或直接用 ArrayList<T>。
示例:通用 JSON 反序列化方法
public static <T> T fromJson(String json, Class<T> clazz) throws JsonProcessingException {
return new ObjectMapper().readValue(json, clazz);
}
调用:fromJson("{\"name\":\"Alice\"}", User.class) —— 类型信息通过 Class 对象带入运行时,绕过擦除限制。
规避常见陷阱:重载、数组与异常类
擦除会导致签名冲突或语法非法,必须提前规避:
- 不要写两个仅泛型不同的重载方法,如
void handle(List<String>)和void handle(List<Integer>),编译失败; - 避免
T[] arr = new T[10],改用List<T>或Object[] + 显式转型; - 泛型类不能继承 Exception,因为 JVM 异常处理机制依赖运行时可识别的类名,擦除后无法区分
MyError<String>和MyError<Integer>。
需要运行时泛型信息?用 TypeToken 技巧
当反射获取父类泛型(如继承自 ArrayList<User>)时,可用匿名子类保留签名:
Type type = new TypeToken<List<User>>() {}.getType();
// 再通过 ParameterizedType 提取实际类型参数
这是 Gson、Jackson 等库解析嵌套泛型的核心手法,本质是利用类加载时保留的 Signature 属性,而非依赖对象实例。


















