理解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,有效平衡类型约束与代码复用需求。

相关推荐
xiguolangzi20 分钟前
mysql迁移PG库 主键、唯一处理、批量修改
java·后端
摇滚侠21 分钟前
Spring Boot3零基础教程,Actuator 导入,笔记82
java·spring boot·笔记
DolphinScheduler社区23 分钟前
小白指南:Apache DolphinScheduler 补数据功能实操演示
java·大数据·开源·apache·海豚调度·大数据工作流调度
TDengine (老段)23 分钟前
TDengine 数据函数 TAN 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
TDengine (老段)24 分钟前
TDengine 数学函数 SQRT 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·1024程序员节
lang2015092831 分钟前
Spring XML AOP配置实战指南
xml·java·spring
桦说编程33 分钟前
深入解析CompletableFuture源码实现(3)———多源输入
java·性能优化·源码阅读
xiaozaq35 分钟前
java 正则表达式 所有的优先级
java·开发语言·正则表达式
鹿邑网爬39 分钟前
Python 制作“满屏浪漫弹窗”教程
后端·python
风一样的美狼子41 分钟前
仓颉语言核心数据结构-高性能与类型安全的工程实践
java·服务器·前端