泛型、通配符以及PECS法则

本文介绍了Java泛型中的PECS原则,即Producer Extends和Consumer Super,详细解析了向上转型和向下转型泛型对象引用的限制。通过示例代码展示了? extends和? super在生产者和消费者场景中的使用,强调了泛型在编译时提供类型安全的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

最近在阅读JDK源码的时候,经常在各种类、接口或者方法上看到各种复杂的泛型写法,不能很好地理解参数这样设计的好处或者意图是什么,因此在阅读相关博客资料后做如下总结归纳。

泛型

  • JDK5引入的新特性,提供了编译时类型安全检测机制。
  • 本质是参数化类型。
  • 好处是在编译时能够检查类型安全,并且所有的强制转换都是自动和隐式的。

受限泛型

有时候我们希望像使用普通类型那样使用泛型类型。

  • 向上转型一个泛型对象的引用
  • 向下转型一个泛型对象的引用

首先要说明的一点是,Java强制在创建对象的时候必须给类型参数制定具体的类型,不能使用通配符,比如new ArrayList<?>()或者new ArrayList<? extends A>这种。

<? extends E>

@Test
	public void test1() {
        //PE:Prodcer Extends
		ArrayList<? extends Number> list = new ArrayList<>();
        
		//list.add(new Integer((1))); 编译错误
		Number number = list.get(0);
	}

只能读取,不能写入!

调用add()会导致编译失败,是因为我们在创建实例时,可能使用了E也可能使用了E的子类,编译器无法获取准确的信息,工作无法正常进行。而对于get()方法来说,由于我们是通过E或者E的子类来创建实例,而用超类来引用子类在Java中是合法的,所以我们能通过get()方法拿到一个E类型的引用。

<? super E>

@Test
	public void test2() {
		//CS:Consumer Super
		ArrayList<? super Apple> list = new ArrayList<>();

		list.add(new Apple());
		//Apple apple = list.get(0);  编译错误
	}

只能写入,不能读取!

调用get()方法会导致编译失败,是因为我们在创建实例时,可能使用了E也可能使用了E的超类,当调用get()时编译器是无法知道准确信息的,而Java中超类引用子类的合法性又无法在这里得到应用(未知类的父类也是未知的)。而对于add()方法,虽然我们无法知道实例到底使用了E还是E的超类,但是我们可以确定加入E或者E的子类是没有问题的,因为超类引用子类在Java中是一个合法行为。

PECS法则

指 “Producer Extends, Consumer Super”,来自 Joshua Bloch的《Effective Java》

换言之,生产者使用 ? extends,消费者使用 ?super。

  • 如果你想要从一个数据类型里获取数据,那么使用 ? extends 通配符。
  • 如果你想要把对象写入一个数据类型,那么使用 ? super 通配符。
  • 如果你既想存,又想取,那就别使用泛型通配符。

参考

Java泛型中<? extends E>和<? super E>的区别 --博客园

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值