我们知道<?>表示:我想使用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准则相关推荐

  1. java pecs_Java 泛型 PECS

    在stackoverflow上看到两篇关于java泛型 PECS 的问答: PECS Remember PECS:"Producer Extends,Consumer Super" ...

  2. Java 泛型的读写规则:PECS

    PECS 是 "Producer Extends Consumer Super" 的缩写,是 Java 泛型中的重要用法. PECS 就是当你需要遍历某一个类型和子类的集合数据时, ...

  3. Java泛型中的PECS原则

    今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: pu ...

  4. Java泛型之PECS原则

    转载自: [Java]泛型中 extends 和 super 的区别? Java泛型详解 文章目录 通配符 上下界通配符的副作用 上界<? extends T>不能往里存,只能往外取 下界 ...

  5. Java 泛型(Generics) 综述

    一. 引子 一般的类和方法,只能使用具体类型:要么是基本类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大. 多态 算是一种泛化机制,但对代码的约束还是太强 ...

  6. Java泛型中extends T和super T的区别?

    <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...

  7. Java泛型中? 和 ? extends Object的异同分析

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 刘一手 来源 | 公众号「锅外的大佬」 Jav ...

  8. java 泛型 t_Kotlin(2) 泛型与集合

    前言 以一个java老鸟的角度,如何去看 kotlin.Java源代码应该如何用Kotlin重构.如何正确学习kotlin并且应用到实际开发中.本文将会探究. 本文分两大块,重难点和潜规则. 重难点: ...

  9. 一文搞懂 Java 泛型,非常详细!

    作者: ZiWenXie http://www.ziwenxie.site/2017/03/01/java-generic/ 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广 ...

最新文章

  1. 统计学 计算机论文,统计学专业论文范文
  2. Hbase完全分布式的搭建
  3. A. [2021.1.29多校省选模拟11]最大公约数(杜教筛/数论)
  4. 算法入门经典习题第一章
  5. C / C++ 经典代码和常考类型
  6. 工业控制系统ICS网络安全简析
  7. 你还在为文件读写而烦恼?Python已经轻松帮你解决了(建议学习)
  8. Word 2003特殊符号录入与编辑(转)
  9. 在云端开展深度学习正逢其时
  10. Mysql-mmm配置全自动切换主从关系和读写分离
  11. [python][project][爬虫] 堆糖网图片下载
  12. Sechunter移动应用隐私合规检测详解
  13. 智能化场景识别,8个视频图像处理方案你值得拥有
  14. sin函数对照表_三角函数值对照表
  15. COM高级应用-Automation(自动化)已是昨日黄花不再高级?
  16. [ERROR] Couldn‘t set the case sensitive attribute of the directory “\\?\C:\WSL\“.Reason: Indicates
  17. MariaDB 分区
  18. 『天涯杂谈』 整理后的有关《新概念》英语的学习方法
  19. uniapp 打开高德地图或者百度地图 进行导航
  20. 数据库(增删改查、mysql建课程表)

热门文章

  1. Android 用显示隐藏完成fragment切换
  2. MySQL数据库的分库分表方案
  3. NBA历史上50大巨星1
  4. 创客教育对生物课堂教学的促进作用
  5. 忘性大,关于ACCESS的一点点。
  6. webpack 多页面 html,基于webpack实现多html页面开发框架八 html引入图片打包和公共页面模块复用...
  7. 网络、互联网、因特网的基本概念与组成
  8. 美文鉴赏-《我们从哪里来,到哪里去?》
  9. react项目搭建本地开发环境
  10. Python--元类