上界通配符 ? extends t 专用于安全读取(producer),可获取 t 及其子类型对象,但禁止添加除 null 外的任何元素;下界通配符 ? super t 专用于安全写入(consumer),可向集合添加 t 及其子类型实例,但读取仅能返回 object 类型。

上界通配符 ? extends T 和下界通配符 ? super T 的核心差异,不在“宽窄”,而在**读写能力的明确分工**:一个专为安全读取设计,一个专为安全写入设计。这种限制源于 Java 泛型的类型擦除和编译期类型安全检查机制。
上界通配符 ? extends T:只读,不能写(除 null)
声明如 List extends Number>,表示“某个具体但未知的 Number 子类型集合”,比如可能是 List<integer></integer>、List<double></double> 或 List<bigdecimal></bigdecimal>,但你不知道是哪一个。
-
读取安全:调用
get()返回值可直接赋给Number或其父类(如Object),因为所有子类型都继承自Number,能保证向上转型不丢失信息 -
写入禁止:不能调用
add(new Integer(1)),也不能加new Double(1.0),甚至不能加new Number()(抽象类不可实例化)。因为编译器无法验证你加的元素是否匹配底层那个“未知的具体子类型”——往List<double></double>里塞Integer显然破坏类型安全 -
唯一例外:可以
add(null),因为null是所有引用类型的合法值,不引入类型歧义
下界通配符 ? super T:可写,读取受限
声明如 List super Integer>,表示“某个具体但未知的 Integer 父类型集合”,比如可能是 List<integer></integer>、List<number></number> 或 List<object></object>。
-
写入安全:可以
add(new Integer(42)),也可以add(new AtomicInteger(100))(若其是Integer子类),因为任何Integer实例都能被赋值给它的任意父类引用(Integer → Number → Object) -
读取受限:调用
get()只能接收为Object类型。你无法安全地把它当作Number或Integer使用,因为底层集合可能是List<object></object>,里面存的可能是字符串或任意对象,强转风险高 -
不能混搭:不能往
List super Integer>中加Double或String,它们不是Integer的子类,也不满足“可赋值给父类”的条件
为什么这样设计?PECS 原则是关键
PECS(Producer Extends, Consumer Super)精准概括了使用逻辑:
-
Producer(生产者)用 extends:当你从集合中“产出”元素供外部使用(如遍历、求和、转数组),就用
? extends T—— 它让你放心读,不让你乱写 -
Consumer(消费者)用 super:当你向集合中“消费”新元素(如收集、归并、批量插入),就用
? super T—— 它让你大胆写,但读出来得自己兜底
这不是语法随意规定,而是编译器在泛型擦除前提下,为保障运行时类型安全所做出的最严格且最实用的妥协。










