类型通配符
类型通配符用于处理不确定或受限的类型参数。
无界通配符
特点
- 可匹配任意类型
- 只能读取(作为 Object),不能添加(除 null)
使用示例
Java
// 无界通配符:可接收任意类型的 List
public void printList(List<?> list) {
for (Object item : list) {
System.out.println(item); // 只能作为 Object 读取
}
}
// 使用
printList(Arrays.asList("A", "B"));
printList(Arrays.asList(1, 2, 3));
不能添加元素:List 不知道具体类型,添加元素可能类型不匹配(除 null)。
上界通配符 <? extends T>
特点
- 匹配 T 或 T 的子类
- 只能读取,不能添加(除 null)
使用示例
Java
// 上界通配符:可接收 Number 或其子类
public double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue(); // 安全读取
}
return total;
}
// 使用
sum(Arrays.asList(1, 2, 3)); // List<Integer>
sum(Arrays.asList(1.0, 2.0, 3.0)); // List<Double>
sum(Arrays.asList(1L, 2L, 3L)); // List<Long>
不能添加元素:List<? extends Number> 不知道具体是 Integer 还是 Double,添加可能类型错误。
| 操作 | 支持 |
|---|---|
| 读取 | ✅ 作为 Number |
| 添加 | ❌ 不支持 |
| 添加 null | ✅ null 是所有类型 |
下界通配符 <? super T>
特点
- 匹配 T 或 T 的父类
- 可以添加 T 或 T 的子类
- 读取只能作为 Object
使用示例
Java
// 下界通配符:可接收 Integer 或其父类
public void addNumbers(List<? super Integer> list) {
list.add(1); // ✅ 可以添加 Integer
list.add(2);
list.add(3);
}
// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // List<Number>
List<Object> objects = new ArrayList<>();
addNumbers(objects); // List<Object>
| 操作 | 支持 |
|---|---|
| 添加 T 或子类 | ✅ |
| 读取 | ✅ 只能作为 Object |
PECS 原则
Producer-Extends, Consumer-Super:
- Producer(生产者/读取):用
? extends T - Consumer(消费者/写入):用
? super T
Java
// 从集合读取 → extends
public void process(List<? extends Number> source) {
Number n = source.get(0); // 读取(Producer)
}
// 向集合写入 → super
public void add(List<? super Integer> target) {
target.add(1); // 写入(Consumer)
}
// 复制方法:src 读取(extends),dst 写入(super)
public static <T> void copy(
List<? extends T> src, // 生产者
List<? super T> dest // 消费者
) {
for (T item : src) {
dest.add(item);
}
}
三者对比
| 通配符 | 匹配范围 | 读操作 | 写操作 |
|---|---|---|---|
| 任意类型 | Object | 仅 null | |
| <? extends T> | T 及子类 | T | 仅 null |
| <? super T> | T 及父类 | Object | T 及子类 |
使用场景选择
| 场景 | 通配符 |
|---|---|
| 只读取不写入 | <? extends T> |
| 只写入不读取 | <? super T> |
| 读取和写入都需要 | 使用具体类型 <T> |
| 不关心具体类型 |
要点总结
- PECS:Producer 用 extends,Consumer 用 super
- 需要读写都用具体类型而非通配符
📝 发现内容有误?点击此处直接编辑