java 泛型 PECS准则
我们知道<?>表示:我想使用Java泛型来编写代码,而不是用原生类型;但是在当前这种情况下,我并不能确定下泛型参数的具体类型,因此用?表示任何某种类型。因此,根据我们对通配符的了解,使用无界通配符的泛型类不能够写数据,而在读取数据时,所赋值的引用也只能是 Object 类型。那么,我们究竟如何向泛型类写入、读取数据呢?
《Effective Java2》给出了答案。 PECS: producer(读取)-extends, consumer(写入)-super。换句话说,如果输入参数表示一个 T 的生产者,就使用<? extends T>;如果输入参数表示一个 T 的消费者,就使用<? super T>。总之,通配符类型可以保证方法能够接受它们应该接受的参数,并拒绝那些应该拒绝的参数。 比如,一个简单的 Stack API :
public class Stack<E>{public Stack();public void push(E e);public E pop();public boolean isEmpty();
}
现在要实现pushAll(Iterable src)方法,将实现 Iterable 接口的 src 的元素逐一入栈:
public void pushAll(Iterable<E> src){for(E e : src)push(e)
}
那么问题就来了:假设有一个实例化Stack的对象stack(类型参数被实例化为Number),显然, 我们向这个 stack 中加入Integer型或Float型元素都是可以的,因为这些元素本来就是Number型的。因此,src 就包括但不限于 Iterable与Iterable两种可能。这时,在调用上述pushAll方法时,编译器就会产生type mismatch错误。原因是显而易见的,因为Java中泛型是不变的,Iterable 与 Iterable 都不是 Iterable及其子类型中的一种。所以,我们对pushAll方法的设计就存在逻辑上的问题。因此,应改为
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {for (E e : src)push(e);
}
这样,我们就可以实现将实现Iterable接口的E类型的容器中的元素读取到我们的 Stack 中。
那么,如果现在要实现popAll(Collection dst)方法,将 Stack 中的元素依次取出并添加到 dst 中,如果不用通配符实现:
// popAll method without wildcard type - deficient!
public void popAll(Collection<E> dst) {while (!isEmpty())dst.add(pop());
}
同样地,假设有一个实例化Stack 的对象 stack , dst 为 Collection,显然,这是合理的。但如果我们调用上述的 popAll(Collection dst)方法,编译器会报出type mismatch错误,编译器不允许我们进行这样的操作。原因是显而易见的,因为Collection不是Collection及其子类型的一种。所以,我们对popAll方法的设计就存在逻辑上的问题。因此,应改为
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {while (!isEmpty())dst.add(pop());
}
这样,我们就可以实现将Stack中的元素读取到我们的 Collection 中。在上述例子中,在调用 pushAll方法时,src生产了E实例(produces E instances);在调用popAll方法时 dst 消费了E实例(consumes E instances)。Naftalin与Wadler将PECS称为Get and Put Principle。
此外,我们再来学习一个例子:java.util.Collections的copy方法(JDK1.7),它的目的是将所有元素从一个列表(src)复制到另一个列表(dest)中。显然,在这里,src是生产者,它负责产生T类型的实例;dest是消费者,它负责消费T类型的实例。这完美地诠释了PECS:
// List<? extends T> 类型的 src 囊括了所有 T类型及其子类型 的列表
// List<? super T> 类型的 dest 囊括了所有可以将 src中的元素添加进去的 List种类
public static <T> void copy(List<? super T> dest, List<? extends T> src) {// 将 src 复制到 dest 中int srcSize = src.size();if (srcSize > dest.size())throw new IndexOutOfBoundsException("Source does not fit in dest");if (srcSize < COPY_THRESHOLD ||(src instanceof RandomAccess && dest instanceof RandomAccess)) {for (int i=0; i<srcSize; i++)dest.set(i, src.get(i));} else {ListIterator<? super T> di=dest.listIterator();ListIterator<? extends T> si=src.listIterator();for (int i=0; i<srcSize; i++) {di.next();di.set(si.next());}}
}
因此,输入参数是生产者时,用 ? extends T;输入参数是消费者时,用 ? super T;输入参数既是生产者又是消费者时,那么通配符类型没什么用了:因为你需要的是严格类型匹配,这是不用任何通配符而得到的。无界通配符<?> 既不能做生产者(读出来的是Object),又不能做消费者(写不进去)。
转自 https://blog.csdn.net/justloveyou_/article/details/52420071
java 泛型 PECS准则相关推荐
- java pecs_Java 泛型 PECS
在stackoverflow上看到两篇关于java泛型 PECS 的问答: PECS Remember PECS:"Producer Extends,Consumer Super" ...
- Java 泛型的读写规则:PECS
PECS 是 "Producer Extends Consumer Super" 的缩写,是 Java 泛型中的重要用法. PECS 就是当你需要遍历某一个类型和子类的集合数据时, ...
- Java泛型中的PECS原则
今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: pu ...
- Java泛型之PECS原则
转载自: [Java]泛型中 extends 和 super 的区别? Java泛型详解 文章目录 通配符 上下界通配符的副作用 上界<? extends T>不能往里存,只能往外取 下界 ...
- Java 泛型(Generics) 综述
一. 引子 一般的类和方法,只能使用具体类型:要么是基本类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大. 多态 算是一种泛化机制,但对代码的约束还是太强 ...
- Java泛型中extends T和super T的区别?
<? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...
- Java泛型中? 和 ? extends Object的异同分析
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 刘一手 来源 | 公众号「锅外的大佬」 Jav ...
- java 泛型 t_Kotlin(2) 泛型与集合
前言 以一个java老鸟的角度,如何去看 kotlin.Java源代码应该如何用Kotlin重构.如何正确学习kotlin并且应用到实际开发中.本文将会探究. 本文分两大块,重难点和潜规则. 重难点: ...
- 一文搞懂 Java 泛型,非常详细!
作者: ZiWenXie http://www.ziwenxie.site/2017/03/01/java-generic/ 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广 ...
最新文章
- 统计学 计算机论文,统计学专业论文范文
- Hbase完全分布式的搭建
- A. [2021.1.29多校省选模拟11]最大公约数(杜教筛/数论)
- 算法入门经典习题第一章
- C / C++ 经典代码和常考类型
- 工业控制系统ICS网络安全简析
- 你还在为文件读写而烦恼?Python已经轻松帮你解决了(建议学习)
- Word 2003特殊符号录入与编辑(转)
- 在云端开展深度学习正逢其时
- Mysql-mmm配置全自动切换主从关系和读写分离
- [python][project][爬虫] 堆糖网图片下载
- Sechunter移动应用隐私合规检测详解
- 智能化场景识别,8个视频图像处理方案你值得拥有
- sin函数对照表_三角函数值对照表
- COM高级应用-Automation(自动化)已是昨日黄花不再高级?
- [ERROR] Couldn‘t set the case sensitive attribute of the directory “\\?\C:\WSL\“.Reason: Indicates
- MariaDB 分区
- 『天涯杂谈』 整理后的有关《新概念》英语的学习方法
- uniapp 打开高德地图或者百度地图 进行导航
- 数据库(增删改查、mysql建课程表)