java泛型默认不变,故list不能赋值给list;? extends t通过上界通配符实现协变,允许安全读取t及其子类元素,但禁止写入(除null),保障类型安全。

用 ? extends T 实现集合读取时的协变支持,核心是让方法能安全接收“T 及其任意子类”的集合,同时只允许读取、禁止写入——这正是 Java 泛型弥补“不变性”缺陷的关键机制。
为什么需要它?
Java 泛型默认不协变:即使 Cat extends Animal,List<cat></cat> 也不是 List<animal></animal> 的子类型。直接传参会编译失败。而 List extends Animal> 就成了一个“泛化入口”,既保持类型安全,又接受所有子类列表。
协变读取的典型写法
定义一个只读方法,参数声明为 List extends T>:
- 方法体内可把每个元素当作
T类型使用(例如调用T定义的方法) - 不能调用
add()、set()等修改操作(编译器阻止,避免混入非预期子类) - 可安全赋值给
T类型变量,或传递给接收T的其他方法
实际例子:统一处理动物列表
假设 Animal 是父类,Cat 和 Dog 都继承它:
public void feedAll(List extends Animal> animals) {
for (Animal a : animals) { // ✅ 编译通过:每个元素可视为 Animal
a.eat();
}
// animals.add(new Dog()); // ❌ 编译错误:无法确定具体子类型,禁止写入
}
调用时可传入:feedAll(new ArrayList<cat>())</cat>、feedAll(new ArrayList<dog>())</dog>、feedAll(new ArrayList<animal>())</animal> —— 全部合法。
关键限制与注意事项
- 返回值只能是
T或其父类型(如Object),不能是具体子类型(如Cat),因为编译器不知道实际是什么子类 - 若需同时读写,不能只靠
? extends T;应拆分为两个参数,或改用无界通配符 + 显式类型检查(不推荐) - 它和 PECS 原则一致:“Producer Extends”——作为数据提供者(producer)时用
extends










