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,剩下的就是多写代码。

相关推荐
pengzhuofan1 小时前
Java开发中的AI Prompt管理指南
java·开发语言·prompt
乌暮1 小时前
JavaEE初阶--线程的状态
java·java-ee
悟空码字1 小时前
SpringBoot 整合 RabbitMQ:和这只“兔子”交朋友
java·后端·rabbitmq
雨中飘荡的记忆2 小时前
Java并发工具深度剖析与实战
java
小股虫2 小时前
从零开始:ActiveMQ安装、Java应用实战与Web控制台体验
java·activemq·java-activemq
java_logo2 小时前
RUSTFS Docker 容器化部署指南
java·运维·docker·容器·jenkins·运维开发
uup2 小时前
秒杀系统中的超卖与重复下单问题
java
用户8307196840822 小时前
Spring注入原型Bean,为啥”新“对象“不翼而飞”?
java
初听于你2 小时前
Thymeleaf 模板引擎讲解
java·服务器·windows·spring boot·spring·eclipse