? extends t 表示上界通配符,限定为t或其子类,编译期强制类型安全;只允许读取和赋null,禁止写入具体对象,因实际类型未知;区别于类型参数声明,后者在作用域内具名且可用。

Java 中的上限通配符 ? extends T 表示“某个未知的具体类型,但该类型一定是 T 或其子类”。它不指定具体是什么类型,只划定一个**上界**——编译器只知道它 ≤ T(按继承关系),但不知道它到底是谁。
它怎么限制上界?
限制方式是**静态、编译期强制的类型安全约束**:声明了 ? extends T 的变量或参数,只能被赋予 List<t></t>、List<subt1></subt1>、List<subt2></subt2> 等具体泛型类型的引用,但不能是 List<object></object> 或 List<string></string>(除非 String 是 T 的子类)。
-
List extends Number> list = new ArrayList<integer>();</integer>✅ 合法(Integer 是 Number 子类) -
List extends Number> list = new ArrayList<string>();</string>❌ 编译失败(String 不是 Number 子类) -
List extends Number> list = new ArrayList<number>();</number>✅ 合法(T 自身也满足 “T 或其子类”)
为什么只能读、不能写?
因为“未知具体类型”导致写入不安全。假设你有:
List extends Shape> shapes = new ArrayList<circle>();</circle>
此时底层实际是 ArrayList<circle></circle>,但编译器只看到“某个 Shape 子类”,可能是 Circle、Rectangle,甚至还没写的 Triangle。如果你允许 shapes.add(new Rectangle()),就可能把 Rectangle 塞进一个专为 Circle 设计的列表,破坏类型一致性。
-
Shape s = shapes.get(0);✅ 安全:所有子类都能向上转型为 Shape -
shapes.add(new Circle());❌ 编译错误:无法确认 Circle 是否匹配底层真实类型 -
shapes.add(null);✅ 唯一允许的写入:null 可赋给任意引用类型
和 <t extends u></t> 的区别
二者都用 extends,但用途不同:
-
<t extends number></t>是**类型参数声明**,用于泛型类/方法定义,T 在整个作用域内是确定且可使用的具体类型(如能调用t.doubleValue()) -
? extends Number是**通配符**,用于变量、参数、返回值等“一次性”场景,代表一个不可知、不可命名的子类型,仅支持安全读取操作
典型使用场景
遵循 PECS 原则(Producer Extends, Consumer Super):
- 当方法只从集合中 读取 元素并当作上界类型处理时,用
? extends T - 例如:统计数值总和、打印图形信息、序列化任意子类型对象
- 反例:向集合添加元素、构造新实例、做子类型专属操作(如调用
circle.getRadius())——这些需要明确知道具体类型,不能依赖通配符
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











