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

news/2024/7/7 20:35:53

点击上方“方志朋”,选择“设为星标”

回复”666“获取新整理的面试资料


原创:小姐姐味道(微信公众号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有更深入的了解,可以深入了解一下函数编程相关的这四个接口。哪怕你只是看一下它的定义,也会有一种原来如此的感觉。

热门内容:

  • SpringBoot实现过滤器、拦截器与切片

  • 阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?

  • 腾讯万亿级 Elasticsearch 技术解密

  • 为什么很多SpringBoot开发者放弃了Tomcat,选择了Undertow

  • 天天用事务,但是你知道MySQL事务的实现原理吗?

  • fastjson这么快,为啥老外还是热衷 jackson?

  • 如何优雅的设计java异常

  • Spring MVC+Spring+Mybatis实现支付宝支付功能(图文详解+完整代码)

  • 互联网公司的中年人都去哪了?

  • Github 标星 11.5K!这可能是最好的 Java 博客系统

  • 大批 IDEA 激活码到期之后的乱象...

  • 全面了解 Nginx 主要应用场景

  • 为什么微服务一定要有网关?

  • 那些在一个公司死磕了5-10年的人,最后都怎么样了?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡


http://lihuaxi.xjx100.cn/news/275933.html

相关文章

飞机的“黑色十分钟”能被人工智能消灭吗?

【导读】近年来&#xff0c;“AI的应用和落地”逐渐成了具化的关键词&#xff0c;它和很多事物很多行业结合在一起&#xff0c;形成了奇妙的“化学反应”。例如&#xff0c;在日常生活中&#xff0c;AI可以推送我们喜欢的新闻或视频&#xff0c;可以在拍照的时候识别场景提升照…

基于3D边界特征的视觉定位技术

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达本文转自&#xff1a;计算机视觉life3D空间中的边界特征线是机器视觉感知中的一种重要的特征&#xff0c;在形状分析、视觉定位、网格简化、栅格创建、非照片级的渲染和表…

十进制转换成十六进制c语言 链栈,C语言 链栈 实现十进制转换二进制,八进制,十六进制...

C语言 链栈 实现十进制转换二进制,八进制,十六进制 (3页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;11.90 积分最后运行结果&#xff1a;代码&#xff1a;#include #include typedef struct Node{…

burpsuite 设置https_新手教程:如何使用Burpsuite抓取手机APP的HTTPS数据

* 本文原创作者&#xff1a;smartdone&#xff0c;本文属FreeBuf原创奖励计划&#xff0c;未经许可禁止转载1.所需条件 手机已经获取root权限 手机已经成功安装xposed框架 电脑一台2.详细步骤2.1 在手机上面安装xposed JustTrustMeJustTrustMe是一个去掉https证书校验的xposed …

.net基础问题

string sqlstr "select BranchCode,BranchName from t_sys_Branch where Jglx_DataDm{0} and IsVisible1"; sqlstr string.Format(sqlstr, departType); 上述代码运行之后 sqlstr"select BranchCode,BranchName from t_sys_Branch where Jglx_DataDmdepartTyp…

TIOBE 9 月编程语言排行榜发布,C++ 增速最快,C++20 的功劳?

转自 | 机器之心编辑 | 小舟、魔王C20 能让 C 复兴吗&#xff1f;TIOBE 公布 2020 年 9 月的编程语言排行榜&#xff0c;C 位列第四&#xff0c;仅次于 C、Java 和 Python。而且 C 相比去年同期增长 1.48%&#xff0c;成为增长最快的编程语言。TIOBE 编程社区指数是衡量编程语言…

2w字长文,让你瞬间拥有「调用链」开发经验

点击上方“方志朋”&#xff0c;选择“设为星标”回复”666“获取新整理的面试资料很多同学表示&#xff0c;对于微服务中常用的调用链功能的原理&#xff0c;感觉很模糊。本文将真正的从零开始&#xff0c;介绍调用链客户端开发的一些要点。让你瞬间拥有APM开发经验。文章很长…

华为腾讯百度众安微众360大咖齐聚,2019中国区块链开发者大会首批议程曝光!...

作者 | Aholiab出品 | 区块链大本营&#xff08;blockchain_camp&#xff09;随着区块链被定义为国家战略&#xff0c;区块链技术得到升温。据有关国际研究机构预测&#xff0c;三年后全球区块链市场规模将达到139.6亿美元&#xff08;约合986.23亿元人民币&#xff09;&#x…