Java泛型通配符入门教程

Java泛型是JDK5中引入的一个新特性,泛型提供了编译是类型安全检测机制,这个机制允许开发者在编译是检测非法类型。泛型的本质就是参数化类型,就是在编译时对输入的参数指定一个数据类型。

泛型带来的好处:

1.类型安全:编译是检查类型是否匹配,避免了ClassCastexception的发生。

2.消除代码强制类型转换:减少了一些类型转换操作。

3.代码复用:可以支持多种数据类型,不要重复编写代码,例如:我们常用的统一响应结果类。

4.增强可读性:通过类型参数就直接能看出要填入什么类型。

第1分钟:为什么需要通配符?

假设你有一个方法要打印所有类型的List:

java 复制代码
// 只能打印List<String>
void print(List<String> list) { ... } 

// 想要打印 List<String>, List<Integer>, List<Fruit>...

通配符就是解决"我需要一个能装任何类型的List"这个问题。


第2分钟:三种通配符,记住这个表格

通配符 通俗叫法 你能做什么 你不能做什么
List<?> 无界通配符 只读 ,只能当Object 什么都加不了 (除了null
List<? extends Apple> 上界通配符 只读 ,拿出来就是Apple 什么都加不了 (除了null
List<? super Apple> 下界通配符 只写 ,能加Apple和它的子类 读出来只能当Object

"上界"和"下界"?

想象类型层次图 ,越往上越抽象(Object在顶端):

java 复制代码
        Object (顶层)
          ↑
       Fruit
       ↑   ↑
    Apple Orange
      ↑
   RedApple

第3分钟:三个核心规则(背下来!)

规则1: ? extends ------ 只能读,不能写

List<? extends T> 是生产者,因为它"吐"数据

java 复制代码
List<? extends Apple> basket = new ArrayList<RedApple>();
Apple a = basket.get(0);      // ✅ 拿出来一定是Apple
basket.add(new Apple());      // ❌ 编译错误!
// basket可能是List<RedApple>,你加Apple进去不安全

记住 :extends = 外取(extends → extract)


规则2: ? super ------ 只能写,不能读(精确读取)

List<? super T> 是消费者,因为它"吃"数据

java 复制代码
List<? super Apple> basket = new ArrayList<Fruit>();
basket.add(new Apple());      // ✅ 安全,Apple是Fruit的子类
basket.add(new RedApple());   // ✅ 安全,RedApple是Apple的子类
Apple a = basket.get(0);      // ❌ 编译错误!
// basket可能是List<Object>,拿出来可能是任何东西

记住 :super = 里放(super → supply)

List<? super Apple>可能是 List<Apple> 或者 List<Fruit> List<Object>。因为不确定,所以我往里面放元素时候,只能放Apple 或者子类RedApple。如果我往里面放Fruit元素,可能它实际是List这个时候就有问题

关键点 :因为不确定 它到底是哪种,编译器必须保证所有可能的情况都类型安全

为什么能放 AppleRedApple(子类)

  • 如果实际是 List<Apple>:直接匹配
  • 如果实际是 List<Fruit>AppleFruit 的子类,可以向上转型安全放入
  • 如果实际是 List<Object>:任何对象都可以放入

无论哪种情况,放 AppleRedApple 都100%安全

总结:? super Apple 的消费者视角,就是------我只接收 Apple 家族的食物,其他一律不行。

规则3: <?> ------ 只能读,且只能当Object用

List<?>List<? extends Object> 的语法糖!

java 复制代码
List<?> basket = new ArrayList<String>();
Object o = basket.get(0);     // ✅ 只能当Object
basket.add("hello");          // ❌ 编译错误!
// 编译器完全不知道?是什么,什么都不让加

记住? = 彻底只读


第4分钟:什么时候用哪个?(PECS原则)

  • P roducer Extends(生产者用extends)

  • 你的方法从List里拿数据用? extends T

  • 示例:遍历、读取、计算

  • C onsumer Super(消费者用super)

    • 你的方法往List里放数据? super T
    • 示例:填充、添加、写入

快速判断 :看方法里get()多还是add()多?


第5分钟:新手最容易踩的坑

java 复制代码
// ❌ 错误:想同时读写
void badMethod(List<? extends Apple> list) {
    list.add(list.get(0)); // 编译错误!
}

// ✅ 正确:根据需求选择
void readMethod(List<? extends Apple> list) { /* 只读 */ }
void writeMethod(List<? super Apple> list) { /* 只写 */ }

终极口诀

上界Stream流,下界Collect收


课后作业(5秒钟自测)

这个方法该用什么通配符?

java 复制代码
void copy(List<___> src, List<___> dest) {
    dest.add(src.get(0)); // 从src读,往dest写
}

答案:src? extends Tdest? super T。这就是Collections.copy()的签名!


恭喜你,入门了! 记住那个表格和PECS,剩下的就是多写代码。

相关推荐
heartbeat..2 小时前
Servlet 全面解析(JavaWeb 核心)
java·网络·后端·servlet
vx_bisheyuange3 小时前
基于SpringBoot的疗养院管理系统
java·spring boot·后端
村口曹大爷3 小时前
JDK 24 正式发布:性能压轴,为下一代 LTS 铺平道路
java·开发语言
1.14(java)3 小时前
MySQL数据库操作全攻略
java·数据库·mysql
正远数智3 小时前
深度解析:SRM系统如何赋能采购库存协同
java·lowcode
青岛少儿编程-王老师3 小时前
CCF编程能力等级认证GESP—C++1级—20251227
java·c++·算法
hashiqimiya4 小时前
java程序的并发
java·开发语言·python
.try-4 小时前
cssTab卡片式
java·前端·javascript
ulias2124 小时前
多态理论与实践
java·开发语言·前端·c++·算法
幽络源小助理5 小时前
SpringBoot+Vue攀枝花水果在线销售系统源码 | Java项目免费下载 – 幽络源
java·vue.js·spring boot