实际开发中的协变与逆变案例:数据处理流水线

在实际开发中,协变(? extends T逆变(? super T 常常出现在处理数据流的场景中。我们通过一个简单的例子——设计一个通用的数据处理流水线,来深入理解它们的使用。


需求描述

假设我们在做一个电商项目,需要处理不同类型的订单数据:

  1. 数据源会提供各种类型的订单(如普通订单、会员订单、促销订单等)。
  2. 我们需要把这些订单传递到处理器进行处理。
  3. 不同的处理器可能对父类或子类的数据有不同的操作需求。

基本类型定义

我们有一个基本的订单类和几个子类:

// 父类:订单
class Order {private String id;public Order(String id) { this.id = id; }public String getId() { return id; }
}// 子类:普通订单
class RegularOrder extends Order {public RegularOrder(String id) { super(id); }
}// 子类:会员订单
class MemberOrder extends Order {public MemberOrder(String id) { super(id); }
}

设计目标:通用数据处理流水线

我们要设计两个通用的方法:

  1. 读取订单数据的工具(协变场景)。
  2. 将订单数据传递给处理器(逆变场景)。

1. 协变案例:只读订单数据

**场景:**我们需要一个工具方法,可以接受任意类型的订单列表(普通订单、会员订单等),并读取这些订单信息,但不对列表内容做修改。

代码实现:

// 工具方法:读取订单列表
public static void readOrders(List<? extends Order> orders) {for (Order order : orders) {System.out.println("订单ID:" + order.getId());}
}

协变的意义:

  • ? extends Order 表示“接受 Order 及其子类列表”。
  • 我们只读取列表里的数据,不向列表中添加任何内容。

调用示例:

List<RegularOrder> regularOrders = List.of(new RegularOrder("R001"), new RegularOrder("R002"));
List<MemberOrder> memberOrders = List.of(new MemberOrder("M001"), new MemberOrder("M002"));// 读取普通订单
readOrders(regularOrders);// 读取会员订单
readOrders(memberOrders);

协变的关键点:

  • 允许传入子类型列表(如 List<RegularOrder>)。
  • 只能“读”,不能“写”。
    • 如果试图往列表中添加元素,编译器会阻止。
orders.add(new RegularOrder("R003")); // 编译报错:不能添加数据!

2. 逆变案例:处理订单数据

**场景:**我们有一个订单处理器,需要把普通订单传递进去进行处理操作。由于不同的处理器可能接收更通用的 Order 类型,我们希望代码更灵活。

代码实现:

// 工具方法:添加订单到处理器
public static void processOrders(List<? super RegularOrder> orders) {orders.add(new RegularOrder("R003")); // 安全添加普通订单System.out.println("订单已处理!");
}

逆变的意义:

  • ? super RegularOrder 表示“接受 RegularOrder 及其父类的列表”。
  • 我们可以向列表中添加 RegularOrder 或其子类的实例。

调用示例:

List<Order> allOrders = new ArrayList<>(); // 容器可以是父类类型
processOrders(allOrders);

逆变的关键点:

  • 允许操作父类列表(如 List<Order>)。
  • 只能“写”,不能安全地“读”。

为什么不能读?
读取的数据类型不确定,只能当作 Object 处理:

Object obj = orders.get(0); // OK:只能当作 Object

3. 协变和逆变结合的设计

我们设计一个通用的数据处理流水线,既能从数据源读取订单,又能将订单传递给处理器。

完整实现:

public static <T> void processPipeline(List<? extends T> source, // 协变:只读数据源List<? super T> target    // 逆变:只写目标
) {for (T item : source) {System.out.println("读取订单:" + ((Order) item).getId());target.add(item); // 将订单传递到目标列表}
}

调用示例:

List<RegularOrder> regularOrders = List.of(new RegularOrder("R001"), new RegularOrder("R002"));
List<Order> allOrders = new ArrayList<>();processPipeline(regularOrders, allOrders);System.out.println("处理后的订单数:" + allOrders.size());

结果输出:

读取订单:R001
读取订单:R002
处理后的订单数:2

总结:协变与逆变的最佳场景

  • 协变(? extends T

    • 适合只读操作,数据来源多样化。
    • 典型场景:数据采集、读取列表内容。
  • 逆变(? super T

    • 适合只写操作,数据目标灵活多样。
    • 典型场景:数据存储、操作父类容器。
  • 协变与逆变结合

    • 适合需要“读取 + 写入”分工明确的场景,如数据流传输、数据转换。

用协变和逆变设计代码,不仅让代码更灵活安全,还能显著提升复用性和可维护性!

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
  • HTTP、HTTPS、Cookie 和 Session 之间的关系
  • 什么是 Cookie?简单介绍与使用方法
  • 什么是 Session?如何应用?
  • 使用 Spring 框架构建 MVC 应用程序:初学者教程
  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
  • 如何理解应用 Java 多线程与并发编程?
  • 把握Java泛型的艺术:协变、逆变与不可变性一网打尽
  • Java Spring 中常用的 @PostConstruct 注解使用总结
  • 如何理解线程安全这个概念?
  • 理解 Java 桥接方法
  • Spring 整合嵌入式 Tomcat 容器
  • Tomcat 如何加载 SpringMVC 组件
  • “在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
  • “避免序列化灾难:掌握实现 Serializable 的真相!(二)”
  • 如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
  • 解密 Redis:如何通过 IO 多路复用征服高并发挑战!
  • 线程 vs 虚拟线程:深入理解及区别
  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
  • “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
  • Java 中消除 If-else 技巧总结
  • 线程池的核心参数配置(仅供参考)
  • 【人工智能】聊聊Transformer,深度学习的一股清流(13)
  • Java 枚举的几个常用技巧,你可以试着用用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476073.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32与CS创世SD NAND(贴片SD卡)结合完成FATFS文件系统移植与测试是一个涉及硬件与软件综合应用的复杂过程

一、前言 在STM32项目开发中&#xff0c;经常会用到存储芯片存储数据。 比如&#xff1a;关机时保存机器运行过程中的状态数据&#xff0c;上电再从存储芯片里读取数据恢复&#xff1b;在存储芯片里也会存放很多资源文件。比如&#xff0c;开机音乐&#xff0c;界面上的菜单图…

Matlab实现海鸥优化算法优化随机森林算法模型 (SOA-RF)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1内容介绍 海鸥优化算法&#xff08;Seagull Optimization Algorithm, SOA&#xff09;是一种基于海鸥群体行为的新型元启发式优化算法。SOA通过模拟海鸥在寻找食物时的飞行模式和集体行动来探索解空间&#xff0c;寻找最优…

C# Postman或者PostApi调试前端webapi接口发送带有request/body/head信息

知识&#xff1a; 前端接口&#xff0c;表单形式提交。 req.ContentType "application/x-www-form-urlencoded"; x-www-form-urlencoded 是一种常见的 MIME 类型&#xff0c;用于将键值对编码为 HTTP 请求体中的 URL 编码格式。在 Web API 中&#xff0c;x-www-for…

npm上传自己封装的插件(vue+vite)

一、npm账号及发包删包等命令 若没有账号&#xff0c;可在npm官网&#xff1a;https://www.npmjs.com/login 进行注册。 在当前项目根目录下打开终端命令窗口&#xff0c;常见命令如下&#xff1a; 1、登录命令&#xff1a;npm login&#xff08;不用每次都重新登录&#xff0…

案例精选 | 某知名教育集团基于安全运营平台的全域威胁溯源实践

某知名教育集团成立于1999年&#xff0c;总部位于北京海淀中关村。集团专注于K-12基础教育&#xff0c;构建了从幼儿园到高中的全面教育体系&#xff0c;涵盖学校管理、教学科研、师资培训、信息化服务等多个方面。集团在全国范围内设有15所小学、12所初中、9所高中、6个国际部…

鸿蒙多线程开发——线程间数据通信对象01

1、线程间通信 线程间通信指的是并发多线程间存在的数据交换行为。由于ArkTS语言兼容TS/JS&#xff0c;其运行时的实现与其它所有的JS引擎一样&#xff0c;都是基于Actor内存隔离的并发模型提供并发能力。 对于不同的数据对象&#xff0c;在ArkTS线程间通信的行为是有差异的&…

徒手从零搭建一套ELK日志平台

徒手从零搭建一套ELK日志平台 日志分析的概述日志分析的作用主要收集工具集中式日志系统主要特点采集日志分类ELK概述初级版ELK终极版ELK高级版ELKELK收集日志的两种形式 搭建ELK平台Logstash工作原理Logstash核心概念环境准备安装部署docker添加镜像加速器安装部署Elasticsear…

React基础知识一

写的东西太多了&#xff0c;照成csdn文档编辑器都开始卡顿了&#xff0c;所以分篇写。 1.安装React 需要安装下面三个包。 react:react核心包 react-dom:渲染需要用到的核心包 babel:将jsx语法转换成React代码的工具。&#xff08;没使用jsx可以不装&#xff09;1.1 在html中…

【FPGA开发】ZYNQ中PS与PL交互操作总结、原理浅析、仿真操作

文章目录 PL与PS交互综述交互端口性能&特点&#xff08;选择方案的凭据&#xff09;GPIO-AXI_GPDMA-DMACHP-AXI_HPACP-AXI_ACP 数据交互实验GP通过BRAMPS为主机&#xff0c;读写BRAMPL作为主机&#xff0c;读写BRAM DMA方式交互 PL与PS交互综述 网络上关于PS PL交互的教程…

【论文笔记】Large Brain Model (LaBraM, ICLR 2024)

Code: https://github.com/935963004/LaBraM Data: 无 目录 AbstractIntroductionMethodNeural tokenizer training&#xff1a;Pre-training LaBraM&#xff1a; ResultsExperimental setup&#xff1a;Pre-training result&#xff1a;Comparison with SOTA&#xff1a;Pre-t…

推荐几个 VSCode 流程图工具

Visual Studio Code&#xff08;简称VSCode&#xff09;是一个由微软开发的免费、开源的代码编辑器。 VSCode 发布于 2015 年&#xff0c;而且很快就成为开发者社区中广受欢迎的开发工具。 VSCode 可用于 Windows、macOS 和 Linux 等操作系统。 VSCode 拥有一个庞大的扩展市…

2024信创数据库TOP30之达梦DM8

近年来&#xff0c;中国信创产业快速崛起&#xff0c;其中数据库作为基础软件的重要组成部分&#xff0c;发挥了至关重要的作用。近日&#xff0c;由DBC联合CIW/CIS共同发布的“2024信创数据库TOP30”榜单正式揭晓&#xff0c;汇聚了国内顶尖的数据库企业及产品&#xff0c;成为…

将网站地址改成https地址需要哪些材料

HTTPS&#xff08;安全超文本传输协议&#xff09;是HTTP协议的扩展。它大大降低了个人数据&#xff08;用户名、密码、银行卡号等&#xff09;被拦截的风险&#xff0c;还有助于防止加载网站时的内容替换&#xff0c;包括广告替换。 在发送数据之前&#xff0c;信息会使用SSL…

RPC安全可靠的异常重试

当调用方调用服务提供方&#xff0c;由于网络抖动导致的请求失败&#xff0c;这个请求调用方希望执行成功。 调用方应该如何操作&#xff1f;catch异常再发起一次调用&#xff1f;显然不够优雅。这时可以考虑使用RPC框架的重试机制。 RPC框架的重试机制 RPC重试机制&#xff1…

【c++丨STL】priority_queue(优先级队列)的使用与模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、priority_queue简介 二、priority_queue的使用 构造函数(constructor) empty size top push和pop swap 仿函数的使用 三、prio…

【数据结构】【线性表】【练习】删除链表倒数第n个结点

目录 申明 题目 分析题目信息 解题思路 代码解析 技巧解析&#xff1a;创建虚拟头结点 时间复杂度分析 思考&#xff1a;能否只用一趟扫描实现&#xff1f; 双指针 双指针解题思路 代码解析 申明 该题源自力扣题库19&#xff0c;文章内容&#xff08;代码&#xff0c…

Ubuntu20.04升级glibc升级及降级的心路历程

想使用pip安装Isaac Sim&#xff0c;无奈此方法只支持 GLIBC>2.34 。使用的是Ubuntu20.04&#xff0c;使用 ldd --version 查看GLIBC版本&#xff0c;如果版本低于 2.34 则需要升级GLIBC&#xff0c;基于此开始了长达一天的尝试。 请注意&#xff0c;升级GLIBC是一个危险操作…

uniapp实现开发遇到过的问题(持续更新中....)

1. 在ios模拟器上会出现底部留白的情况 解决方案&#xff1a; 在manifest.json文件&#xff0c;找到开源码视图配置&#xff0c;添加如下&#xff1a; "app-plus" : {"safearea":{"bottom":{"offset" : "none" // 底部安…

Electron开发构建工具electron-vite(alex8088)添加VueDevTools(VitePlugin)

零、介绍 本文章的electron-vite指的是这个项目&#x1f449;electron-vite仓库&#xff0c;electron-vite网站 本文章的VueDevTools指的是VueDevTools的Vite插件版&#x1f449;https://devtools.vuejs.org/guide/vite-plugin 一、有一个用electron-vite创建的项目 略 二、…

机器学习基础05_随机森林线性回归

一、随机森林 机器学习中有一种大类叫集成学习&#xff08;Ensemble Learning&#xff09;&#xff0c;集成学习的基本思想就是将多个分类器组合&#xff0c;从而实现一个预测效果更好的集成分类器。集成算法大致可以分为&#xff1a;Bagging&#xff0c;Boosting 和 Stacking…