java泛型是编译期特性,运行时通过类型擦除替换为原始类型或上界(如object/number),导致list与list运行时均为list、无法new t[]、instanceof t非法;通配符? extends t用于安全读取,? super t用于安全写入;桥接方法解决多态,静态泛型和泛型异常因擦除失效。

练泛型高级面试题,核心不是背答案,而是理解“为什么这么设计”和“不这么写会出什么问题”。重点落在擦除机制、边界限制、通配符语义、以及与继承/重载/静态成员的冲突上。
紧扣类型擦除反推行为
Java泛型是“编译期特性”,运行时只剩原始类型。所有高级题几乎都绕不开这点:
- 问“
List<string></string>和List<integer></integer>运行时是否同一类型?”——答:是,都是List,擦除后无区别 - 问“为什么不能
new T[10]?”——答:T 擦除为 Object,但数组创建需明确运行时类型,JVM 不允许创建Object[]后再当成String[]用(会破坏类型安全) - 问“
instanceof T为什么不合法?”——答:T 在运行时不存在,无法做类型判断
吃透通配符的三种写法和适用场景
别只记符号,要能对应实际需求:
-
?(无界):只读操作,比如void print(List> list),只要能遍历就行 -
? extends T(上界):取数据安全,比如Number max(List extends Number> nums),保证 get 出来一定是 Number 或子类 -
? super T(下界):存数据安全,比如void addInts(List super Integer> dest),可以放心 add(Integer),因为 dest 至少能装 Integer 及其父类(如 Number、Object)
典型陷阱题:“List<string></string> 能否赋值给 List extends Object>?”——可以;但“能否赋值给 List super String>?”——也可以;而“能否赋值给 List<object></object>?”——不行,这是常见错误点。
动手写桥接方法和类型冲突案例
面试官爱考“为什么这段代码编译失败”,背后常涉及泛型与继承/重载的交互:
- 写一个泛型类
Node<t></t>,再写子类MyNode extends Node<integer></integer>,观察编译后生成的桥方法(bridge method) - 尝试定义
static <t> T getValue()</t>—— 编译失败,因为静态上下文无法绑定实例化的 T - 尝试让类同时实现
Comparable<string></string>和Comparable<integer></integer>—— 编译失败,擦除后都是Comparable,接口重复
对比真泛型语言建立认知锚点
拿 C# 或 Rust 的泛型对比 Java,能快速定位“伪泛型”的代价和权衡:
- C# 泛型在运行时保留类型信息,支持
typeof(T)、可创建T[]、可对值类型做零开销泛型 - Java 选择擦除,是为了让
ArrayList新旧代码二进制兼容——老 jar 包不用改就能跑在 JDK 5+ 上 - 所以 Java 泛型不能支持基本类型(得用包装类)、不能做运行时类型分支(
if (T == String.class)不成立)
练到这个程度,遇到“为什么泛型不能用于异常类型”“为什么不能用泛型声明枚举常量”这类题,你自然能从擦除 + JVM 规范 + 设计取舍三层讲清楚。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











