Java泛型是编译期类型检查机制,运行时类型信息被擦除:List<String>和List<Integer>字节码中均为List;类型参数擦除为上界或Object;导致无法instanceof判断、不能创建泛型数组、静态成员不可用类型参数。

Java 泛型不是语法糖,是编译期的类型检查机制,运行时会被擦除(Type Erasure),这是理解所有泛型问题的核心。
泛型擦除到底擦掉什么
编译后,泛型信息全部消失:List<String> 和 List<Integer> 在字节码里都是 List;泛型类、泛型方法的类型参数被替换为上界(如 <T extends Comparable> 擦除为 Comparable),没有上界则替换成 Object。这意味着:
- 不能用 instanceof 判断泛型类型,比如 if (list instanceof ArrayList<String>) 编译不通过
- 不能创建泛型数组,new ArrayList<String>[10] 会报错,因为运行时无法保证类型安全
- 静态方法/字段不能直接使用类的类型参数(如 static T value; 是非法的)
通配符 ? 和边界怎么选
问“什么时候用 ? extends T,什么时候用 ? super T”?记住 PECS 原则(Producer Extends, Consumer Super):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 只往外取数据(读)→ 用 ? extends T(如 List<? extends Number> 可以 get() 出 Number 或其子类,但不能 add() 任何具体对象)
- 只往里放数据(写)→ 用 ? super T(如 List<? super Integer> 可以 add(Integer),但 get() 只能拿到 Object)
- 既读又写 → 就别用通配符,直接用具体类型,比如 List<Integer>
泛型方法和类型推断常见陷阱
泛型方法声明在返回值前加 <T>,比如 <T> T getValue(T t)。注意:
立即学习“Java免费学习笔记(深入)”;
- 类型推断依赖实参,不是返回值。下面代码会报错:String s = Utils.parse(); 因为没传参,编译器无法推断 T
- 若实参是 null,推断可能失败或退化为 Object,建议显式指定:Utils.<String>parse()
- 泛型方法可重载,但仅靠返回类型不同不行(如 <T> T get() 和 <T> List<T> get() 不能共存)
为什么泛型不能继承,但可以实现?
泛型类本身不是类型,而是模板。ArrayList<String> 和 ArrayList<Integer> 没有继承关系,它们共享原始类型 ArrayList。但接口可以多态:
- List<String> list = new ArrayList<String>(); ✅ —— 因为 ArrayList 实现了 List 接口
- List<String> list = new LinkedList<String>(); ✅ —— 同样是接口实现关系
- ArrayList<String> list = new ArrayList<Object>(); ❌ —— 类型不兼容,即使 Object 是 String 的父类

















