这一次,终于弄懂了协变和逆变
一、前言
刘大胖决定向他的师傅灯笼法师请教什么是协变和逆变。
刘大胖:师傅,最近我在学习泛型接口的时候看到了协变和逆变,翻了很多资料,可还是不能完全弄懂。
灯笼法师:阿胖,你不要被这些概念弄混,编译器可不知道你说的什么协变逆变。这个问题,首先你得弄懂什么叫类型的可变性。
刘大胖:可变性?
二、可变性
灯笼法师:对,可变性是以一种类型安全的方式,将一个对象作为另一对象来引用。虽然是可变,但其实对象的引用地址是不会变的,只是忽悠下编译器。
刘大胖:师傅说的将一个对象作为另一对象来引用?这不就是继承么?
灯笼法师:是的,你可以看下面代码演示(C#):
刘大胖:哦,我理解了,由于MemoryStream继承于Stream,所以MemoryStream的对象可以变为Stream的对象,原来我天天在接触可变性,我竟然不知道。
灯笼法师:是的,这种转变其实遵守了里氏替换原则,爱徒,你可还记得?
刘大胖:当然,为了面试早已烂熟于心。里氏替换原则(LSP):指的是所有引用基类的地方都可以使用其子类的对象。可是师傅,这个和协变逆变有什么关系呢?
三、协变
灯笼法师:协变和逆变只是可变性的分类,主要用于泛型接口和委托中。协变逆变只是类型转换的方向不同。我们先看下接口协变吧,假如有Apple类继承于Fruit,如下:
灯笼法师:然后现在写了一个打印水果名称的方法,如下:
灯笼法师:这时如果你打算打印一些苹果的名称,你会怎么写?
刘大胖:这不是很简单,Apple继承自Fruit,那可以直接使用PrintFruit类了。撸了下,怎么报错了?代码如下:
灯笼法师:大胖,你要理清楚,虽然Apple继承Fruit,但List<Apple>和List<Fruit>却一点关系也没有,如图:
刘大胖:那如果这样,岂不是要为每一种水果都要定义一个PrintFruit方法,我觉得官方不会不知道这个问题吧?
灯笼法师:这种问题,官方当然知道了,所以才有了泛型接口的协变用以支持List<Apple>自动转为List<Fruit>。C#中使用out表示泛型参数的可协变性,List没有out约束,所以不能协变,但它的基类IEnumable却实现了,如图:
灯笼法师:所以只要把PrintFruit的参数类型换成IEnumable就可以了,如图:
刘大胖:那为什么List<T>不能加out以支持协变呢?
灯笼法师:爱徒问的好,List继承于IEnumable,它比IEnumable更宽泛,它支持读和写,但协变只能可读,主要用于约束输出参数。
刘大胖:好吧,我回去再消化下。师傅你再讲一下什么是逆变吧。
四、逆变
灯笼法师:逆变是相反的,即支持List<Fruit>转为List<Apple>,泛型接口上添加in约束输入参数。
刘大胖:有点懞,师傅你还是用代码吧!
灯笼法师:好吧,假如现在我要让苹果列表或桔子列表可以按名称排序,需要一个定义一个水果比较器,此比较器能用于任何种类的水果列表,代码如下:
灯笼法师:现在给苹果和桔子列表按名称排序吧,代码如下:
刘大胖:师傅你别忽悠我,Sort的参数可是要具体类型的比较器的,你看代码:
灯笼法师:大胖,就这是逆变,以使得基类的泛型对象替代子类的泛型对象,主要是因为IComparer<T>中使用了in关键字来约束,代码如下:
五、总结
刘大胖:哦,我有点明白了,协变就是支持泛型子类自动转泛型父类,逆变就是支持泛型父类自动转泛型子类。
灯笼法师:也可以这么理解,但这些转换只是针对编译器,其引用地址并没有改变。
翻外篇1:
协变:String =>Object
逆变:Object => String
翻外篇2:
灯笼法师在刘大胖走后从背后拿出手机,屏幕上显示来不及关闭的知乎APP:
把复杂的技术简单的写出来,更多文章请关注我的公众号:
这一次,终于弄懂了协变和逆变相关推荐
- 计算机考研英语一和英语二的区别,考研英语一和英语二的区别 今天终于弄懂了!...
原标题:考研英语一和英语二的区别 今天终于弄懂了! 大家在最后三个月冲刺需要注意: 1.建议留几套真题,做考前模拟,精读真题可以用 <考研圣经>(英语二用)98-07 年的真题,都是逐词逐 ...
- 淘宝特价版拉新赚钱的页面怎么做?我终于弄懂了
淘宝的同胞兄弟特价版,虽然长的朴实无华以至于经常被人问起淘宝特价版靠谱吗?2021年淘宝特价版可谓大火了一把,阿里巴巴不计成本的大力推广淘宝特价版,目的也非常明确要把拼多多占领的市场掠夺回来.最近还传 ...
- 秒懂Kotlin之协变(Covariance)逆变(Contravariance)与抗变(Invariant)
[版权申明] 非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/108708218 出自:shusheng0 ...
- .NET可变性解析(协变和逆变)
[一]何为可变性 可变性是.NET4.0中的一个新特性,可变性可分为 : 协变性.逆变性.不可变性. 那么在.NET4.0之前是否有可变性? 答案是肯定的,我们可以通过下面的几个实例来简单的了解一下. ...
- java协变 生产者理解_Java进阶知识点:协变与逆变
一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...
- 对协变和逆变的简单理解
毕业快一年了,边工作边学习,虽说对.net不算精通,但也算入门了,但一直以来对协变和逆变这个概念不是太了解,上学时候mark了一些文章,今天回过头看感觉更糊涂了,真验证本人一句口头禅"知道的 ...
- Scala入门到精通——第二十一节 类型参数(三)-协变与逆变
本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} .当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S] ...
- Java进阶知识点:协变与逆变
一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...
- TypeScript 协变和逆变
TypeScript 协变和逆变 文章目录 TypeScript 协变和逆变 前言 内涵和外延 LSP(里氏替换原则) 定义 理解 第一层 第二层 第三层 技巧 参考 前言 内涵和外延 说协变和逆变前 ...
最新文章
- python中登录、注册操作数据库
- JavaWeb学习总结(六)—HttpServletResponse
- 使用halcon将一个圆上的点拟合成圆形并且求出圆心
- python画端午节_我想带你去旅行,我用Python提前做了一份端午旅游攻略,请收下!...
- mysql函数封装_PHP访问MYSQL数据库封装类(附函数说明)
- (王道408考研数据结构)第二章线性表-第二节2:顺序表的操作
- 物联网打工人必备:LiteOS Studio图形化调测能力
- oracle 库not null,oracle平添not null约束
- 【服务器】【个人网盘】宝塔安装OneIndex
- IOS程序之发送短信代码实现
- php事务和回滚,php – Mysql事务:提交和回滚
- 分享一个好的数据集资源目录
- 网络安全中的恶意软件
- ABSynthe : 侧信道攻击加密函数窃取密钥
- sql语句日期格式转换
- 2020 数学建模国赛 B 题参考思路
- WIN10-x86虚拟机镜像-32位-VMware(亲测可用)
- 浪潮m6智能服务器,浪潮全新M6服务器满足智慧时代算力需求
- Android从零开始搭建MVVM架构(3)——ViewModel
- 树莓派pico w点灯
热门文章
- linux内核的冒险md来源释义# 14raid5非条块读
- C程序优化之路(二)
- 用计算机算算术平方根顺序是ON然后是什么,第2课时用计算器求一个正数的算术平方根.ppt...
- outlook日历不显示_如何在Outlook Online中突出显示不同的日历
- 10以内数的组成分解图_大班数学教案《10以内数的组成》
- matlab练习程序(二值图像连通区域标记法,一步法)
- 如何让程序跑起来――第三章
- Android TimeAnimator
- AUTH password
- CSS 特殊性、继承与层叠