当前位置: 首页 > news >正文

Java泛型通配符详解:搞懂?/extends/super用法,避开集合操作踩坑点

上次跟你们聊了泛型的基础用法,今天接着往下说 —— 泛型里还有个挺重要的概念叫 “通配符”,就是那个问号 “?”,很多人第一次见都懵:这玩意儿跟普通泛型有啥区别?为啥有时候非得用它不可?小索奇当初也卡这儿好久,后来拿实际例子一琢磨,才彻底明白它的用处。

先给你出个题:假如你写了个方法,要打印所有 List 集合里的元素,不管这个 List 装的是 String、Integer 还是别的类型,该咋写?

你可能会想,直接用 List不就行了?比如这样:

public static void printList(List list) {

for (T item : list) {

System.out.println(item);

}

}

这么写没问题,但还有个更简洁的方式 —— 用通配符 “?”:

public static void printList (List<?> list) {

for (Object item : list) {

System.out.println (item);

}

}

看出区别没?用通配符的话,不用定义泛型参数,代码更短。而且这两种写法效果一样,不管你传 List还是 List,都能正常打印。那为啥要有两种写法呢?其实核心区别在于:如果方法里不需要用到泛型的具体类型,只用通配符就够了;要是需要操作具体类型(比如给集合加元素),就得用

比如你想写个方法,给 List 里加一个默认元素,这时候用通配符就不行了:

// 这段代码会编译报错!

public static void addDefault (List<?> list) {

list.add ("默认值"); // 报错:无法确定?的类型,不能加 String

}

这背后的原因是啥呢?因为通配符 “?” 代表 “未知类型”,编译器不知道这个 List 到底装的是啥,自然不敢让你随便加元素 —— 万一人家是 List,你硬塞个 String 进去,不就乱套了?

但用就能搞定:

public static void addDefault(List list, T defaultValue) {

list.add(defaultValue);

}

调用的时候指定类型就行,比如给 List加默认值:

List strList = new ArrayList<>();

addDefault (strList, "默认值"); // 没问题

是不是一下子就懂了?简单说就是:通配符适合 “只读” 场景(比如打印集合),泛型参数适合 “读写” 场景(比如给集合加元素)。

除了单纯的 “?”,通配符还有两种常用写法:<? extends T > 和 <? super T>,这俩也特容易搞混,小索奇当初记了好几天才分清。

先看 <? extends T>,它代表 “T 及其子类”。比如 <? extends Number>,就包括 Integer、Double、Long 这些 Number 的子类。这种写法的特点是 “能读不能写”(跟普通?类似,但多了类型限制)。比如你写个方法,求所有 Number 类型集合的总和:

public static double sum (List<? extends Number> list) {

double total = 0;

for (Number num : list) {

total += num.doubleValue ();

}

return total;

}

不管你传 List还是 List,都能算总和,因为它们都是 Number 的子类。但还是不能往里面加元素,比如 list.add (10) 会报错 —— 因为编译器不知道具体是 Integer 还是 Double,怕加错类型。

再看 <? super T>,它代表 “T 及其父类”。比如 <? super Integer>,就包括 Number、Object 这些 Integer 的父类。这种写法的特点是 “能写不能读”(或者说读出来是 Object 类型)。比如你想写个方法,给 List 里加多个 Integer 元素:

public static void addIntegers (List<? super Integer> list) {

list.add (1);

list.add (2); // 没问题,因为?至少是 Integer 类型

}

调用的时候,传 List、List甚至 List都能行。但读元素的时候,只能用 Object 接收:

for (Object obj : list) {

// 要想用 Integer,得手动强转

Integer num = (Integer) obj;

}

小索奇之前做统计功能时,就用 <? super Integer> 存过数据 —— 既可以存到 Integer 列表,也能存到 Number 列表,灵活度特别高。

最后给你总结个小口诀,记起来更方便:“上界 extends 能读不能写,下界 super 能写不能读,通配符?只读不写,泛型读写都能搞”。

你们平时写代码时,有没有用过通配符却踩了坑的?比如想加元素加不进去,或者不知道该用 extends 还是 super?可以在评论区跟小索奇聊聊,咱们一起把泛型这点事儿彻底搞明白~

搜索即兴小索奇,点击关注,加入社区群聊,获取更多好用工具和资源

http://www.xdnf.cn/news/1424323.html

相关文章:

  • 二、感知机
  • 高防IP防护效果评估全攻略:从指标解读到实战测试
  • langgraph / openmanus / suna 对比
  • 数据安全不用愁,群晖NAS让你存得放心、用得安心
  • 深度学习环境搭建运行(二) Ubuntu22.04安装基于CUDA11.8的ONNXRuntime-gpu1.18.1详细步骤(新手入门)
  • 联邦学习的文献复现与创新思路指导
  • Qt 项目文件(.pro)中添加 UI 文件相关命令
  • 深度学习】--卷积神经网络
  • k8s--etcd
  • h5实现内嵌微信小程序支付宝 --截图保存海报分享功能
  • authentication port-control auto 和 dot1x port-control auto
  • Linux ARP老化机制/探测机制/ip neigh使用
  • Paimon MergeTreeWrite、Compaction 和 快照构建
  • 甲烷浓度时空演变趋势分析与异常值计算(附下载脚本)
  • 基于docker-compose搭建EFK(Elasticsearch+fluentd+kibana)的日志平台
  • 2025年工作后值得考的财会行业证书推荐,尤其是第二个!
  • 从网络层接入控制过渡到应用层身份认证的过程
  • 如何在SptingBoot项目中引入swagger生成API文档
  • HarvardX TinyML小笔记2(番外3:数据工程)
  • 技术速递|构建你的第一个 MCP 服务器:如何使用自定义功能扩展 AI 工具
  • Linux之Shell编程(四)函数、数组、正则
  • PostgreSQL备份指南:逻辑与物理备份详解
  • EPLAN如何添加接触器辅助触头 | 解决触点不足问题详解4----使用部件组
  • 三、Gitee平台使用指南
  • 在Lumerical FDTD中,磁偶极子通常用于激发TE模式,而电偶极子用于激发TM模式(文心一言)
  • chrome好用的浏览器插件
  • 51.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--登录注册扩展
  • UE角色取消被Decal影响
  • 在 PHP 应用中处理限流和 API 节流:扩展、防滥用的最佳实践
  • 【数据可视化-102】苏州大学招生计划全解析:数据可视化的五大维度