Java tips
一句话记住
PECS:Producer Extends, Consumer Super
- 往外读 →
extends- 往里写 →
super
核心概念:箱子的只读/只写
把泛型容器想象成箱子:
| 通配符 | 记忆口诀 | 箱子特性 | 你能做什么 |
|---|---|---|---|
<? extends T> | extends = 读取 | 里面可能是 T 的任何子类 | 只能拿出来(读),不能放进去 |
<? super T> | super = 写入 | 里面可以装 T 的任何父类 | 只能放进去(写),拿出来只能是 Object |
<? extends T> - 上界通配符
含义:这个箱子装的是 T 或 T 的子类,但具体是什么不知道。
List<? extends Number> list = new ArrayList<Integer>();
// ✅ 可以读 - 拿出来肯定是 Number
Number n = list.get(0);
// ❌ 不能写 - 不知道箱子实际是 Integer、Double 还是其他
// list.add(10); // 编译错误!
// list.add(3.14); // 编译错误!为什么不能写?list 实际可能是 ArrayList<Integer>,你往里面塞个 Double,运行时就会爆炸。编译器直接禁止。
<? super T> - 下界通配符
含义:这个箱子装的是 T 或 T 的父类,你可以安全地放入 T。
List<? super Number> list = new ArrayList<Object>();
// ✅ 可以写 - Integer、Double 都是 Number 的子类,肯定能装
list.add(10);
list.add(3.14);
// ❌ 不能读成具体类型 - 可能是 Object,可能是 Number
// Number n = list.get(0); // 编译错误!
Object obj = list.get(0); // 只能当成 Object为什么不能读成 Number?list 实际可能是 ArrayList<Object>,里面装个 String 也是合法的,你当成 Number 用就会崩溃。
PECS 实战
// 生产者:从集合读取数据 → 用 extends
public void printNumbers(List<? extends Number> list) {
for (Number n : list) {
System.out.println(n);
}
}
// 消费者:往集合写入数据 → 用 super
public void fillNumbers(List<? super Number> list) {
list.add(100);
list.add(3.14);
}
// 同时使用
public void copy(List<? extends Number> src, List<? super Number> dst) {
for (Number n : src) {
dst.add(n);
}
}对比总结
List<Integer> integers = Arrays.asList(1, 2, 3);
// extends - 只读
List<? extends Number> readOnly = integers;
Number n = readOnly.get(0); // ✅
// readOnly.add(10); // ❌ 编译错误
// super - 只写(读出来是 Object)
List<? super Number> writeOnly = new ArrayList<Object>();
writeOnly.add(100); // ✅
writeOnly.add(3.14); // ✅
// Number n = writeOnly.get(0); // ❌ 编译错误
Object o = writeOnly.get(0); // ✅| 场景 | 选用 |
|---|---|
| 只读不写 | <? extends T> |
| 只写不读 | <? super T> |
| 又读又写 | 不用通配符,直接用 List<T> |
速查表
读取数据 → extends → Producer
写入数据 → super → Consumer
extends: 只能 get,不能 add(除了 null)
super: 只能 add,get 出来是 Object



