原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。

温馨提示:泛型相关。以下内容请在安静的场所、充足的时间下查看,因为它非常的绕,容易把人绕晕。

PECS的全程是Producer Extends Consumer Super,第一次听说,我一脸懵逼。但看到jdk中越来越多的泛型代码,我决定去了解一下。

java的泛型,只在编译期有效。也就是说,编译之后的字节码,已经抹除了泛型信息。

其实,对于常年接触业务代码的同学来说,泛型用的并不是特别多。当你使用设计模式设计代码,或者在设计一些比较底层的框架时,肯定会碰到这个问题。

一个例子

泛型该怎么写?我们首先看一下jdk中的一些例子。

java.util.function.Consumer

@FunctionalInterface
public interface Consumer<T> {void accept(T t);default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}

java8的interface新增了staticdefault方法,我们不去过多关注。你会发现,里面有<T,R>字样,<? super V, ? extends T>字样等。

那什么时候该用super,什么时候该用extends?这就是PECS原则。

为了解释这个原理,我们创建三个类。

A,B,C。

其中。A extends B,B extends C。

static class A extends B{}
static class B extends C{}
static class C {}

然后,我们使用测试类测试一下。

static class Example<T>{}public static void main(String[] args) {{Example<? extends A> testAA = new Example<A>();Example<? extends A> testAB = new Example<B>();//报错Example<? extends A> testAC = new Example<C>();//报错Example<? extends B> testBA = new Example<A>();Example<? extends B> testBC = new Example<C>();//报错Example<? extends C> testCA = new Example<A>();Example<? extends C> testCB = new Example<B>();}{Example<? super A> testAA = new Example<A>();Example<? super A> testAB = new Example<B>();Example<? super A> testAC = new Example<C>();Example<? super B> testBA = new Example<A>();//报错Example<? super B> testBC = new Example<C>();Example<? super C> testCA = new Example<A>();//报错Example<? super C> testCB = new Example<B>();//报错}}

为了更直观一些,我们截个idea的图。

我们返回头来再看<? extends T>,只要后面的new,声明的是T的子类或者T本身,那么都是没错的。反之,如果是它的父类,则报错。这很好理解,后半部分的实例,一定要能够全面覆盖前面的声明。这也就是Producer-Extends,它可以对外提供对象(难以理解的概念)。

接下来我们看一下<? super T>。只要是T的父类或者T本身,都没有什么问题,甚至可以是Object。比如,下面的代码就不会报错。

Example<? super C> testCO = new Example<Object>();

根据字面意思,Consumer-super也比较晦涩,如果设计的类是消费者,那应该用super关键字为此类型指定一个子类。

这张图只画了声明部分的原则。为了配合上面这张图,进行更精细的理解,我们创建一个7层的继承关系。

static class Parent1{}
static class Parent2 extends Parent1{}
static class Parent3 extends Parent2{}static class T extends Parent3{}static class Child1 extends T{}
static class Child2 extends Child1{}
static class Child3 extends Child2{}

同时,我们创建两个集合容器进行验证。

List<? extends T> extendsT = new ArrayList<>();List<? super T > superT = new ArrayList<>();

以下代码运行都是没有问题的。

List<? super T > superT = new ArrayList<>();
superT.add(new T());
superT.add(new Child1());
superT.add(new Child2());
superT.add(new Child3());

我们把代码分成两部分,一部分是泛型集合的声明部分。一部分是实例的初始化部分。可以看到,? super T界定了最小子类是T,则声明部分的最小类就是T,ArrayList后面的<>,可以是T的任何父类。但是,当向里面添加元素时,初始化的却是T的子类

再来看extendsT。当我们往里添加数据的时候,无一例外的报错了。

extendsT.add(new T());
extendsT.add(new Child1());
extendsT.add(new Parent1());
extendsT.add(new Parent2());
extendsT.add(new Object());

那是因为,extendsT中存放的其实是T的一种子类(现象),如果我们去添加元素,其实不知道到底应该添加T的哪个子类,这个时候,在进行强转的时候,肯定会出错。但是如果是从集合中将元素取出来,我们则可以知道取出来的元素肯定是T类型(全是它的子类)。

接下来,我们再强行分析一下 ? super T。superT中,因为的都是类型T的父类(容器),所以如果去添加T类或者T的子类(操作),肯定没什么问题。但是如果将元素取出来,则不知道到底是什么类型,所以superT可以添加元素但是没法取出来。

按照我们以往的经验,extendsT只出不进,属于生产者一类;superT只进不出,属于消费者。这也就有了我们上面所提到的“Producer Extends Consumer Super”,也就是PECS原则。

这个过程可真是绕,我认为这是定义非常失败的一个名词。

End

现在,再来看我们文章头部jdk的类Consumer,是不是有了新的理解?其实,这个函数是和函数编程相关的。java8的四个核心函数接口有:Function、Consumer、Supplier、Predicate。

Function<T, R> T:入参类型,R:出参类型。

Consumer<T> T:入参类型;没有出参。

Supplier<T> T:出参类型;没有入参。

Predicate<T> T:入参类型;出参类型Boolean。

想要对PECS有更深入的了解,可以深入了解一下函数编程相关的这四个接口。哪怕你只是看一下它的定义,也会有一种原来如此的感觉。

作者简介:小姐姐味道  (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。我的个人微信xjjdog0,欢迎添加好友,进一步交流。

近期热门文章

《996的乐趣,你是无法想象的》
魔幻现实主义,关爱神经衰弱

《一切荒诞的傲慢,皆来源于认知》
不要被标题给骗了,画面感十足的消遣文章

《必看!java后端,亮剑诛仙》
后端技术索引,中肯火爆。全网转载上百次。

《学完这100多技术,能当架构师么?(非广告)》
精准点评100多框架,帮你选型

面试常问的PECS原则,到底是什么鬼?相关推荐

  1. java中高级面试_中高级面试常问:Java面向对象设计的六大原则

    这篇文章主要讲的是面向对象设计中,我们应该遵循的六大原则.只有掌握了这些原则,我们才能更好的理解设计模式.我们接下来要介绍以下6个内容.单一职责原则--SRP 开闭原则--OCP 里式替换原则--LS ...

  2. 计算机考研复试面试常问问题 数据库篇

    计算机考研复试面试常问问题 数据库篇 在复习过程中,我用心查阅并整理了在考研复试面试中可能问到的大部分问题,并分点整理了答案,可以直接理解背诵并加上自己的语言润色!极力推荐打印下来看,效率更高!绝对良 ...

  3. 测开工程师-面试常问测试用例

    测开工程师-面试常问测试用例 一.网络测试的一般流程 二.弱网功能测试 四.微信红包测试 五.无网状态测试 三.微信朋友圈点赞测试 六.登陆界面测试 七.直播APP测试 八.微信搜索界面测试 九.水杯 ...

  4. 测试岗位面试常问知识点(偏HR)

    面试常问 视情况回答 1.一个优秀的测试工程师应该具备什么品质? 2.为什么想做测试相关的工作? 对测试感兴趣,性格适合(细心.耐心等),测试人员是质量的保证者,喜欢挑战.展开分析. 3.测试开发与测 ...

  5. 数据库面试常问——for考研复试面试

    关于数据库的一些面试常问问题 前言: 本人22考研党,已上岸,发一些复试准备整理的资料作为对考研准备的一个收尾.由于近几年基本都是线上复试,线上的话会更加注重概念的考察,本人在复试准备期间搜集了面试题 ...

  6. 计算机考研复试面试常问问题 操作系统篇

    计算机考研复试面试常问问题 操作系统篇 在复习过程中,我用心查阅并整理了在考研复试面试中可能问到的大部分问题,并分点整理了答案,可以直接理解背诵并加上自己的语言润色!极力推荐打印下来看,效率更高! 此 ...

  7. java8 垃圾收集_面试官:怎么做JDK8的垃圾收集器的调优(面试常问)

    看着面试官真诚的眼神,心中暗想看起来年纪轻轻却提出如此直击灵魂的问题.擦了擦额头上汗,我稍微调整了一下紧张的情绪,对面试官说: 在jdk8中有serial收集器.parallel收集器.cms收集器. ...

  8. GET 和 POST 的区别(重要,面试常问)

    GET 和 POST 的区别(重要,面试常问) 1.GET 在浏览器回退时是无害的,而 POST 会再次提交请求. (get:不会再次发送请求:post:浏览器会继续向URI发送请求) 2.GET 产 ...

  9. 给大家提供一些面试常问的问题

    给大家提供一些面试常问的问题 1. 简述 private. protected. public. internal 修饰符的访问权限. 答 . private :     私有成员, 在类的内部才可以 ...

最新文章

  1. (0092)iOS开发AVAudioRecorder录制音频
  2. 淘淘商城FTP服务器、Redis和solr集群配置
  3. 张小龙宣布微信小程序可直接从桌面进入
  4. me21n增强BADI:ME_PROCESS_PO_CUST之process_account
  5. led流水灯——51单片机程序
  6. 九十七、轻松搞定Python中的PDF办公自动化系列
  7. sed: -e expression #1, unknown option to `s'解决办法
  8. 容器编排技术 -- Kubernetes 为 Namespace 配置最小和最大 CPU 限制
  9. aspx 修改了样式但是在点击按钮后被刷新_135编辑器使用教程|动画按钮到底在哪里啊?...
  10. GitHub 的“封神”之路!
  11. PyCharm 专题
  12. VC无闪烁刷屏技术的实现【转】
  13. 经济机器是怎样运行的(文字版)-瑞.达利欧
  14. IP Scanner Pro for mac(局域网IP扫描)
  15. 【转载】log4j日志
  16. SQLServer2000数据库导入步骤
  17. python列表功能默写_python 1 默写用递归实现无限极分类 2 默写用树实现无限极分类...
  18. win10新建文本改html乱码,win10专业版文字乱码该怎么办?解决方法是什么?
  19. 云计算设计模式翻译(五):Compute Resource Consolidation Pattern
  20. 百度地图——多点标记及路线

热门文章

  1. iOS 面试基础题目
  2. sd卡受损格式化后还能恢复数据吗?
  3. 手机连接投影机的步骤_手机怎么连接投影仪?这几招实用
  4. 小米是否真的可以干翻华为?
  5. 半导体物理-固体晶格结构
  6. 微信小程序 申请第三方平台流程
  7. 自动抢红包-辅助功能的使用
  8. 转载 usb_alloc_coherent 和 usb_free_coherent
  9. 送礼蓝牙耳机哪款合适?2021最好的蓝牙耳机排行!
  10. 服务器控件与客户端控件