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

相关推荐
空中湖几秒前
Spring AI 多模型接入实战:OpenAI、Ollama、通义千问切换只需改配置
java·人工智能·spring
tianyatest3 分钟前
表格分类统计及排序
java·excel·暖通
为啥全要学29 分钟前
自注意力中随着根号dk的增大,qk点积的方差为什么也会增大
python·深度学习
万亿少女的梦16830 分钟前
基于Spring Boot与MySQL的乡镇群众服务系统设计与实现
java·spring boot·mysql·权限管理·前后端分离
thefool11226642 分钟前
Java 面向对象
java
2501_9481069144 分钟前
计算机毕业设计之jsp-智慧旅游分享平台
java·开发语言·spark·汽车·课程设计·旅游
来两个炸鸡腿1 小时前
【Datawhale2607】llm-algo-leetcode task02 基础算子
人工智能·python·大模型
阿里云云原生1 小时前
Agent 不再是“玩具”!AgentScope Java 2.0 GA 发布:构建企业级分布式智能体底座
java·开发语言·分布式·agentscope
4154111 小时前
MyBatis-Plus + PostGIS 实战(四):GeoJSON 序列化与前端地图对接,全局 WKT + 局部 GeoJSON,两种格式优雅共存
java·mybatis·postgis·geojson
峥嵘life1 小时前
Andorid 打开热点,关闭有线网,ifconfig卡死分析解决
java·服务器·网络