上界通配符? extends T用于安全读取——编译器保证集合中所有元素至少为T或其子类,可统一按T类型处理(如调用draw()),但禁止add/set等写入操作,契合PECS原则中“Producer Extends”场景。

上界通配符(? extends T)在绘图系统中主要用于统一读取不同子类型图形对象,比如让一个方法既能处理 List<circle></circle>,也能处理 List<rectangle></rectangle> 或 List<shape></shape>,前提是它们都继承自公共父类(如 Shape)。它的核心价值不是“多态调用”,而是“安全读取”——编译器保证取出的每个元素至少是上界类型,因此可直接当作上界类型使用,无需强制转换。
为什么绘图系统需要上界通配符
绘图系统常有如下类结构:
abstract class Shape { void draw() { ... } }class Circle extends Shape { void draw() { ... } }class Rectangle extends Shape { void draw() { ... } }class Triangle extends Shape { void draw() { ... } }
若不使用通配符,你得为每种图形写单独方法:renderCircles(List<circle>)</circle>、renderRectangles(List<rectangle>)</rectangle>……重复且不可扩展。而用上界通配符,只需一个入口:
public void renderAll(List<? extends Shape> shapes) {<br> for (Shape s : shapes) {<br> s.draw(); // ✅ 安全:s 肯定是 Shape 或其子类,draw() 方法一定存在<br> }<br>}
实际调用时支持哪些列表
以下调用全部合法,编译通过:
renderAll(new ArrayList<Circle>());renderAll(new ArrayList<Rectangle>());renderAll(new ArrayList<Triangle>());renderAll(new ArrayList<Shape>());
但注意:List<Object> 或 List<String> 不行——因为 Object 和 String 不是 Shape 的子类型,不满足 ? extends Shape 约束。
能做什么?不能做什么?
能做的(只读场景):
- 遍历并调用
Shape中定义的方法(如draw()、getArea()、toString()) - 将元素赋值给
Shape类型变量:Shape s = shapes.get(0); - 传递给接收
Shape参数的方法:logShape(s);
不能做的(禁止写入):
-
shapes.add(new Circle());→ 编译错误!编译器不知道具体是哪种子类型列表,加Circle可能破坏List<Rectangle>的类型一致性 -
shapes.set(0, new Triangle());→ 同样报错 - 声明泛型变量时不能用它初始化:
List<? extends Shape> list = new ArrayList<Shape>();合法,但list = new ArrayList<Circle>();也合法;而反过来ArrayList<? extends Shape>无法直接 new,因 ? 是未知具体类型
搭配 PECS 原则理解更清晰
PECS(Producer Extends, Consumer Super)提醒你:
- 如果这个列表是生产者(只往外提供元素),就用 ? extends T —— 绘图系统里,图形列表就是典型的“生产图形对象供渲染”的角色
- 如果要往里存东西(比如收集用户绘制的新图形),应改用下界通配符
? super Circle,而非上界
简单说:渲染图形 → 读取 → ? extends Shape;收集图形 → 写入 → ? super Shape(或更具体的子类型)。

















