java泛型具有不变性,即list不是list的子类型,尽管string是object的子类;为绕过该限制并保障类型安全,需使用通配符:表示完全未知类型。

Java 泛型的不可变性(Invariance)是面试中常被混淆但极易踩坑的核心概念。它不是指“不能修改对象”,而是指泛型类型之间**不存在继承关系**——哪怕其类型参数本身有父子类关系,泛型容器也不自动继承。
为什么 List 不是 List
这是不可变性的典型体现。虽然 String 是 Object 的子类,但 List
原因在于类型安全:如果允许 List<string> stringList = new ArrayList(); List<object> objList = stringList;</object></string>,那么你就能往 objList 里 add(new Date()),这会破坏 stringList 的类型契约,导致运行时隐患——而泛型的设计目标正是在编译期杜绝这类问题。
如何用通配符绕过不可变性限制?
不可变性 ≠ 无法协作。Java 提供了通配符机制,在保持类型安全的前提下实现灵活读写:
-
List extends Number>:只读视图。可安全获取元素(返回类型是Number),但不能 add 除null外的任何值(因为编译器不知道具体是 Integer 还是 Double)。 -
List super Integer>:只写视图。可安全 addInteger或其子类(如Long若存在),但 get() 返回的是Object,需谨慎处理。 -
List>:完全未知类型,只能当作List<object></object>的弱化版使用(get 得到 Object,add 只能加 null)。
常见错误写法与辨析要点
面试官常通过以下代码考察是否真正理解不可变性:
void process(List<object> list) { /* ... */ }
List<string> strs = new ArrayList();
process(strs); // 编译失败!</string></object>
辨析关键:泛型类型是否可替换,看的是整个类型签名是否字面一致或满足通配符约束,而非底层元素类型的继承链。
正确做法是改方法签名:
- 若只需读取 → 改为
void process(List extends Object> list)(等价于List>); - 若需添加任意 Object → 改为
void process(List<object> list)</object>,调用方必须传真正的List<object></object>; - 更通用方案 → 改为泛型方法:
<t> void process(List<t> list)</t></t>。
和协变(Covariance)、逆变(Contravariance)的关系
Java 数组是协变的(String[] 是 Object[] 的子类型),所以能写 Object[] arr = new String[1];,但这导致运行时 arr[0] = new Date() 报 ArrayStoreException —— 安全性后移。
而泛型选择不可变 + 通配符显式声明,把灵活性和安全性都交给开发者在编译期决定。这就是为什么说“泛型比数组更安全,但更严格”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











