理解Java泛型通配符:List<? extends T>与List<? super T>的深度解析

引言

在Java泛型系统中,通配符?是实现类型安全与灵活性的重要工具。其中List<? extends T>List<? super T>的差异常令开发者困惑。本文将通过理论解析、代码示例和实践场景,揭示它们的核心区别及应用场景。

一、基础概念

1.1 上界通配符(Upper Bounded Wildcard)

List<? extends T>声明了一个只读容器 ,表示集合元素是T或其子类:

java 复制代码
void processNumbers(List<? extends Number> list) {
    Number n = list.get(0); // 安全读取
    // list.add(1); 编译错误!
}

此列表无法写入(除null),因实际类型可能是List<Integer>List<Double>

1.2 下界通配符(Lower Bounded Wildcard)

List<? super T>创建了可写容器 ,表示集合元素是T或其父类:

java 复制代码
void fillIntegers(List<? super Integer> list) {
    list.add(42); // 安全写入
    Object obj = list.get(0); // 需强制转型
}

允许添加T类型元素,但读取时仅能获得Object类型。

二、核心差异对比

特性 List<? extends T> List<? super T>
类型边界 元素类型<=T 元素类型>=T
数据流向 生产者(数据输出) 消费者(数据输入)
读取操作 返回具体类型T 返回Object
写入操作 禁止(除null) 允许添加T及其子类
典型应用场景 计算结果/遍历元素 收集数据/回调处理

三、PECS原则实践

3.1 Producer-Extends

当集合作为数据生产者时使用extends:

java 复制代码
double sum(List<? extends Number> nums) {
    return nums.stream()
               .mapToDouble(Number::doubleValue)
               .sum();
}

可接受List<Integer>, List<Double>等参数。

3.2 Consumer-Super

当集合作为数据消费者时使用super:

java 复制代码
void pushAll(List<? super Integer> dest, List<Integer> src) {
    dest.addAll(src); // 安全注入数据
}

允许目标列表为List<Number>List<Object>

四、典型应用场景

4.1 集合工具类实现

观察java.util.Collections.copy()源码:

java 复制代码
public static <T> void copy(
    List<? super T> dest, // 消费者
    List<? extends T> src // 生产者
) {
    for (int i=0; i<src.size(); i++)
        dest.set(i, src.get(i));
}

该设计确保:

  • 源集合安全输出元素

  • 目标集合安全接收元素

4.2 回调处理模式

java 复制代码
interface Processor<T> {
    void process(List<? super T> results);
}

void execute(Processor<? super String> processor) {
    processor.process(new ArrayList<Object>());
}

允许处理器处理更宽泛的结果类型。

五、常见误区解析

5.1 错误写入extends列表

java 复制代码
List<? extends Number> nums = new ArrayList<Double>();
nums.add(1.0); // 编译错误!

编译器无法确认实际类型是否匹配。

5.2 错误读取super列表

java 复制代码
List<? super Integer> list = new ArrayList<Number>();
Integer i = list.get(0); // 需要显式转型
Object obj = list.get(0); // 安全方式

六、进阶应用技巧

6.1 多重边界组合

java 复制代码
<T extends Comparable<? super T>> void sort(List<T> list) {
    // 支持父类实现Comparable的情况
}

允许T或其父类实现比较接口。

6.2 类型推断优化

java 复制代码
static <T> void copy(
    List<? super T> dest,
    List<? extends T> src
) {
    // 更优的类型推断
}

相比非通配符版本,增强API灵活性。

结语

理解extendssuper通配符的本质差异,关键在于把握数据流动方向。遵循PECS原则可使代码兼具类型安全与灵活性。建议在以下场景选择:

  • 需要只读访问时使用? extends T

  • 需要写入操作时使用? super T

  • 同时需要读写时避免使用通配符

通过合理运用这些特性,开发者可以构建出更健壮、更灵活的泛型API,有效平衡类型约束与代码复用需求。

相关推荐
dr李四维4 分钟前
Java在小米SU7 Ultra汽车中的技术赋能
java·人工智能·安卓·智能驾驶·互联·小米su7ultra·hdfs架构
小杨40424 分钟前
python入门系列六(文件操作)
人工智能·python·pycharm
Zach_yuan28 分钟前
list的模拟实现
c++·list
RainbowSea29 分钟前
130道基础OJ编程题之: 78~88
java
松树戈38 分钟前
IDEA Commit 模态提交界面关闭VS开启对比
java·ide·intellij-idea
谦行42 分钟前
前端视角 Java Web 入门手册 4.4:Web 开发基础—— Listener
java·后端
jk_1011 小时前
MATLAB中strip函数用法
java·服务器·数据库
一弓虽1 小时前
maven学习
java·学习·github·maven
24k小善1 小时前
Flink Forward Asia 2024 大会 内容整理
java·大数据·flink
xiaozaq1 小时前
在Eclipse中安装Lombok插件
java·python·eclipse