一、前言

刘大胖决定向他的师傅灯笼法师请教什么是协变和逆变。

刘大胖:师傅,最近我在学习泛型接口的时候看到了协变和逆变,翻了很多资料,可还是不能完全弄懂。

灯笼法师:阿胖,你不要被这些概念弄混,编译器可不知道你说的什么协变逆变。这个问题,首先你得弄懂什么叫类型的可变性。

刘大胖:可变性?

二、可变性

灯笼法师:对,可变性是以一种类型安全的方式,将一个对象作为另一对象来引用。虽然是可变,但其实对象的引用地址是不会变的,只是忽悠下编译器。

刘大胖:师傅说的将一个对象作为另一对象来引用?这不就是继承么?

灯笼法师:是的,你可以看下面代码演示(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. 计算机考研英语一和英语二的区别,考研英语一和英语二的区别 今天终于弄懂了!...

    原标题:考研英语一和英语二的区别 今天终于弄懂了! 大家在最后三个月冲刺需要注意: 1.建议留几套真题,做考前模拟,精读真题可以用 <考研圣经>(英语二用)98-07 年的真题,都是逐词逐 ...

  2. 淘宝特价版拉新赚钱的页面怎么做?我终于弄懂了

    淘宝的同胞兄弟特价版,虽然长的朴实无华以至于经常被人问起淘宝特价版靠谱吗?2021年淘宝特价版可谓大火了一把,阿里巴巴不计成本的大力推广淘宝特价版,目的也非常明确要把拼多多占领的市场掠夺回来.最近还传 ...

  3. 秒懂Kotlin之协变(Covariance)逆变(Contravariance)与抗变(Invariant)

    [版权申明] 非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/108708218 出自:shusheng0 ...

  4. .NET可变性解析(协变和逆变)

    [一]何为可变性 可变性是.NET4.0中的一个新特性,可变性可分为 : 协变性.逆变性.不可变性. 那么在.NET4.0之前是否有可变性? 答案是肯定的,我们可以通过下面的几个实例来简单的了解一下. ...

  5. java协变 生产者理解_Java进阶知识点:协变与逆变

    一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...

  6. 对协变和逆变的简单理解

    毕业快一年了,边工作边学习,虽说对.net不算精通,但也算入门了,但一直以来对协变和逆变这个概念不是太了解,上学时候mark了一些文章,今天回过头看感觉更糊涂了,真验证本人一句口头禅"知道的 ...

  7. Scala入门到精通——第二十一节 类型参数(三)-协变与逆变

    本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} .当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S] ...

  8. Java进阶知识点:协变与逆变

    一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...

  9. TypeScript 协变和逆变

    TypeScript 协变和逆变 文章目录 TypeScript 协变和逆变 前言 内涵和外延 LSP(里氏替换原则) 定义 理解 第一层 第二层 第三层 技巧 参考 前言 内涵和外延 说协变和逆变前 ...

最新文章

  1. python中登录、注册操作数据库
  2. JavaWeb学习总结(六)—HttpServletResponse
  3. 使用halcon将一个圆上的点拟合成圆形并且求出圆心
  4. python画端午节_我想带你去旅行,我用Python提前做了一份端午旅游攻略,请收下!...
  5. mysql函数封装_PHP访问MYSQL数据库封装类(附函数说明)
  6. (王道408考研数据结构)第二章线性表-第二节2:顺序表的操作
  7. 物联网打工人必备:LiteOS Studio图形化调测能力
  8. oracle 库not null,oracle平添not null约束
  9. 【服务器】【个人网盘】宝塔安装OneIndex
  10. IOS程序之发送短信代码实现
  11. php事务和回滚,php – Mysql事务:提交和回滚
  12. 分享一个好的数据集资源目录
  13. 网络安全中的恶意软件
  14. ABSynthe : 侧信道攻击加密函数窃取密钥
  15. sql语句日期格式转换
  16. 2020 数学建模国赛 B 题参考思路
  17. WIN10-x86虚拟机镜像-32位-VMware(亲测可用)
  18. 浪潮m6智能服务器,浪潮全新M6服务器满足智慧时代算力需求
  19. Android从零开始搭建MVVM架构(3)——ViewModel
  20. 树莓派pico w点灯

热门文章

  1. linux内核的冒险md来源释义# 14raid5非条块读
  2. C程序优化之路(二)
  3. 用计算机算算术平方根顺序是ON然后是什么,第2课时用计算器求一个正数的算术平方根.ppt...
  4. outlook日历不显示_如何在Outlook Online中突出显示不同的日历
  5. 10以内数的组成分解图_大班数学教案《10以内数的组成》
  6. matlab练习程序(二值图像连通区域标记法,一步法)
  7. 如何让程序跑起来――第三章
  8. Android TimeAnimator
  9. AUTH password
  10. CSS 特殊性、继承与层叠