/   今日科技快讯   /

近日,小米创始人、董事长兼CEO雷军在抖音上开启了其直播带货的首秀。从晚上8点开播,到晚上10点,销售额就已经破亿。包括1000台售价49999元的透明电视在内的商品一推出便“秒光”售罄。不过,本场直播的“主角”——小米10尊享纪念版,最后仍有部分没有售完。

/   作者简介   /

本篇文章来自Zhujiang的投稿,带大家一起学习下Kotlin之泛型的逆变和协变的知识,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章!

Zhujiang的博客地址:

https://juejin.im/user/3913917127985240

/   前言   /

距离写上篇文章到现在已经一个多月了,时间确实隔得有点久。这一个多月发生了好多事情,从天津辞职到了北京,然后在新公司干了也快一个月了。。。扯远了扯远了。。。

/   故事开始   /

周五的下午,小老弟儿把手里的活都干完了,闲来无事在网上溜达Kotlin相关的知识,还带着华子。

“好大哥,你写的那篇文章中的泛型那块讲到逆变和协变的时候就没写了,你给我讲讲呗,来,好大哥,来根华子!抽这个不咳嗽。”

“华子自己留着抽吧,我咳嗽不来了。逆变和协变是吗?正好我也准备写文章了,那就先给你讲一遍理理思路吧。”

“感谢好大哥!我在网上溜达的时候发现Kotlin中逆变和协变又有两个关键字:in和out,这是啥意思啊?”

“怎么说呢,Kotlin泛型中的逆变和协变感觉和Java泛型中的逆变协变一样啊!”

“啊?Java中也有逆变协变嘛。。。。”

/   Java中的逆变协变   /

“先带你看看Java中的逆变协变吧,Java会了Kotlin就很简单了。”来吧,先来点简单的,咱们慢慢来!

public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}  public void toWork() {System.out.println("我是工人"+getName()+",我要好好干活!!!");}}

新建一个Person类,写了两个属性,还有一个待实现的toWork()方法。

public class Worker1 extends Person {public Worker1(String name, int age) {super(name, age);}@Overridepublic void toWork() {System.out.println("我是1工人"+getName()+",我要好好干活!!!");}
}

Worker1继承自Person类,并重写了toWork()方法。

public class Worker2 extends Person {public Worker2(String name, int age) {super(name, age);}@Overridepublic void toWork() {System.out.println("我是2工人"+getName()+",我也要好好干活!!!");}
}

Worker2没什么好说的,和Worker1一样,只是类名有点区别。

/   协变   /

“现在说的都没问题吧?”

“好大哥,看你说的,这我还能不会嘛!您快继续吧。”

“好,现在问你一个问题,你看下面的代码会报错吗?”

List<Person> personArrayList = new ArrayList<>();
personArrayList.add(new Person("aaa",11));
personArrayList.add(new Worker1("bbb",12));
personArrayList.add(new Worker2("ccc",13));

“肯定不会啊,因为Worker1和Worker2都是Person的子类,所以这么写是可以的!”

“小老弟儿不错哈,那再看看下面的代码会报错嘛?”

 public static void main(String[] args) {List<Person> personArrayList = new ArrayList<>();personArrayList.add(new Person("aaa",11));personArrayList.add(new Worker1("bbb",12));personArrayList.add(new Worker2("ccc",13));List<Worker1> personArrayList1 = new ArrayList<>();personArrayList1.add(new Worker1("ddd",14));List<Worker2> personArrayList2 = new ArrayList<>();personArrayList2.add(new Worker2("eee",15));setWork(personArrayList);setWork(personArrayList1);setWork(personArrayList2);}public static void setWork(List<Person> studentList) {for (Person o : studentList) {if (o != null){o.toWork();}}}

"等会啊,代码有点多,我捋一捋。首先建一个Person的集合,然后放入刚才的数据,然后建一个Worker1和Worker2的集合,再调用setWork()方法,应该是没问题的吧?"

“嘿嘿,小老弟儿,这就有问题了,你看!”

"啊?这是为啥啊,Worker1和Worker2不是Person的子类嘛?"

“Worker1和Worker2是Person的子类,但是List和List并不是List的子类啊!”

“好大哥好大哥,快说说怎么解决吧。”

“这就要进入今天的主题了----协变!上面说过了List和List并不是List的子类,那么咱们需要做的就是让List和List变成List 的子类,做法很简单,只要加上 ? extends就可以了。”

 public static void setWork(List<? extends Person> studentList) {for (Person o : studentList) {if (o != null){o.toWork();}}}

“这么简单吗?”

“对,就这么简单,咱们运行下看看吧!”

我是工人aaa,我要好好干活!!!
我是1工人bbb,我要好好干活!!!
我是2工人ccc,我也要好好干活!!!
我是1工人ddd,我要好好干活!!!
我是2工人eee,我也要好好干活!!!

“哇!真的可以啊!那以后写泛型我就全部都写成协变的不得了!这样所有的子类就都可以这样用了!”

“千万别!这样做肯定是有限制的!协变的泛型只能获取但不能修改了,比如在List中就只能get而不能add了 ,用的时候一定要注意!”

“啊?我不信!”

“哎,你非不信的话咱们就来试试不得了!”

“啊!真的是啊!不对,你往里添加的是Worker1 ,但是泛型是Person!”

“刚刚不是说了嘛!这已经是协变了,是可以的。”

“好大哥,你改成Peoson再试试!”

“好好好,改,你看!”

“啊?为啥啊!好大哥,快说吧,别卖关子了!”

“哈哈哈,行。你想一下啊,咱们刚才把泛型改成了协变的,意思就是上界咱们定为了Person,但是咱们的类型写的是 ? 编译器并不知道咱们给它的具体类型是什么,只要是继承自Person的类就可以,所以get出的对象肯定是Person的子类型,根据多态的特性,所以能够直接赋值给Person,但是add就不可以了,咱们可能添加的是List或List,还有可能是List,所以编译器无法确定咱们添加的到底是什么类型就无法继续执行了,肯定就报错了!”

“奥,这样说我好像有点理解了。”

/   逆变   /

“其实协变搞明白之后逆变就简单了,和协变是正好相反的!咱们再来看一个方法吧!你看看这个会报错嘛!”

          List<Person> personArrayList = new ArrayList<>();personArrayList.add(new Person("aaa",11));personArrayList.add(new Worker1("bbb",12));personArrayList.add(new Worker2("ccc",13));List<Worker1> personArrayList1 = new ArrayList<>();personArrayList1.add(new Worker1("ddd",14));setWorker(personArrayList);setWorker(personArrayList1);public static void setWorker(List<Worker1> studentList) {for (Object o : studentList) {System.out.println("哈哈 "+o.toString());}}

“这个。。。应该会报错,方法接收的是List ,但是传的却有List ,而且 Woker1是Person的子类,而不是Person是Worker1的子类。”

“不错,小老弟儿变聪明了,哈哈哈。确实是不可以的,原因和你说的差不多,但是这种情况下咱们就可以使用逆变了。”

public static void setWorker(List<? super Worker1> studentList) {for (Object o : studentList) {System.out.println("哈哈 "+o.toString());}
}

“好大哥,逆变是不是和协变一样也有限制?”

“没错,逆变和协变一样,类型也是 ?不过 ?extends 是上界通配符,而 ?super 是下界通配符,它的范围包括Worker1和它的父类。和协变相反,逆变中是可以add的,因为Worker1一定是这个未知类型的子类型,所以是可以add的。这里也没有get的限制,会变成Object ,因为在Java中所有类型都是Object的子类。”

“奥,逆变也不难嘛!”

/   Kotlin中的逆变协变   /

“小老弟,其实如果懂Java中的逆变和协变的话那么Kotlin的逆变和协变基本不用学了。。因为基本完全一样!你说不懂Kotlin的逆变和协变其实是不懂Java的逆变和协变。来看下Kotlin的逆变和协变吧!还是先来写几个类,和上面的一样,不过是使用Kotlin编写。”

open class Person(val name: String, val age: Int)  {open fun toWork() {println("我是工人$name,我要好好干活!!!")}
}class Worker1(name: String, age: Int) : Person(name, age) {override fun toWork() {println("我是1工人$name,我要好好干活!!!")}
}class Worker2(name: String, age: Int) : Person(name, age) {override fun toWork() {println("我是2工人$name,我也要好好干活!!!")}
}

Kotlin的协变 - out

“好大哥,说了半天了,in和out关键字到底怎么用你还没说呢!”

“别着急,你看看代码就懂了!”

fun main() {val personArrayList: MutableList<Person> = ArrayList()personArrayList.add(Person("aaa", 11))personArrayList.add(Worker1("bbb", 12))personArrayList.add(Worker2("ccc", 13))val personArrayList1: MutableList<Worker1> = ArrayList()personArrayList1.add(Worker1("ddd", 14))val personArrayList2: MutableList<Worker2> = ArrayList()personArrayList2.add(Worker2("eee", 15))setWork(personArrayList)setWork(personArrayList1)setWork(personArrayList2)
}fun setWork(studentList: List<out Person>) {for (o in studentList) {o.toWork()}
}

“啊,大哥,我好像明白了,这。。。。和Java的协变一样啊,只是关键字不同!”

“哈哈哈,所以说只要懂了Java其实Kotlin并不难!你再仔细看下setWork()这个方法有什么问题。”

“这个方法提醒咱们这里的out关键字可以省略掉!不对啊,那省略了就会报错啊!”

“哈哈哈,这其实就是Kotlin为咱们做的事,Kotlin中List是只读的,所以说肯定是安全的,所以官方在定义List接口的时候就直接定义成了协变的!”

Kotlin的逆变 - in

"好大哥,逆变我觉得我应该会了,我帮你改写下刚才Java逆变的方法吧!"

fun setWorker(studentList: MutableList<in Worker2>) {for (o in studentList) {println("哈哈 " + o.toString())}
}

“没错,就是这么简单!”

/   总结   /

“好大哥,你说为什么会有逆变和协变呢?为什么不可以直接都能使用,非要搞这么复杂呢?”

“你知道泛型的类型擦除吗?”

“额。。。听说过。”

“因为Java的泛型类型在编译的时候会发生类型擦除,为了保证类型安全,所以才。。。。算了,之后再说吧,这个说起来有点多,下回好好给你讲!准备下班了小老弟,明天放假,准备干啥去啊?”

“当然是出去玩啊!”

“疫情期间,尽量少出门吧。下班了,走了。”

如果本文对你有帮助,请别忘记三连啊。如果本文有描述不恰当或错误的地方,请提出来,感激不尽。就这样,下回见。

推荐阅读:

你平时开发会关注卡顿和卡顿率吗?

GDG上海实录回顾,带你快速上手Kotlin协程

LitePal 3.2来了,千呼万唤的索引功能

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

跟着小老弟来学习Kotlin中的逆变和协变相关推荐

  1. java中的逆变、协变、不变概念讲解转载自http://www.cnblogs.com/en-heng/p/5041124.html,感谢编程路上的前辈们!

    En-Heng 无他,但手熟尔 博客园 首页 新随笔 联系 订阅 管理 随笔 - 32  文章 - 0  评论 - 33 Java中的逆变与协变 看下面一段代码 Number num = new In ...

  2. 协变逆变java_Java中的逆变与协变

    什么是逆变与协变 协变(Covariance) 如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变 逆变(Contravariance) 如果B是A的子类,并且F(B)成了F(A)的父类 ...

  3. ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof

    一:如何去学习?都去学习什么? 1:学习优秀项目的设计思想,多问几个为什么,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中 2:学习优秀项目的代码风格,代码的封装设计 ...

  4. Java中的逆变与协变

    学习Java的同学注意了!!!  学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java! 协变和逆变指的是宽类型和窄类型在某种情况下 ...

  5. 教你如何攻克Kotlin中泛型型变的难点(下篇)

    简述: 前几天我们一起为Kotlin中的泛型型变做了一个很好的铺垫,深入分析下类型和类,子类型和子类之间的关系.什么是子类型化关系以及型变存在的意义.那么今天将会讲点更刺激的东西,也就是Kotlin泛 ...

  6. 教你如何攻克Kotlin中泛型型变的难点(应用篇)

    简述: 这是泛型型变最后一篇文章了,也是泛型介绍的最后一篇文章.顺便再扯点别的,上周去北京参加了JetBrains 2018开发者日,主要是参加Kotlin专场.个人感觉收获还是挺多的,bennyHu ...

  7. UPS电源中的逆变电路与Simulink仿真

    一.设计题目: UPS电源中的逆变电路 主要指标及要求: 输入电压240V 输出电压220/230/240V 效率>90% 二.设计思路.方案选择及电路工作原理: 采用单相全桥逆变电路,并使用双 ...

  8. C# - 委托中的逆变

    百度百科逆变的概念,再来练下对逆变的理解. 在下面的示例代码中,定义三个委托方法,一个普通方法 委托方法的签名参数继承于Human,普通方法的参数是父类. 因此,在调用委托时,可把子类转换为父类. 这 ...

  9. 跟着小琼琼学习opencv~

    楼楼最近整理笔记,发现了自己学习Opencv时候的码的代码和简单介绍,现贴上来~主调用方法在最下方.转载请注明出处~ #include <opencv2\xfeatures2d\nonfree. ...

最新文章

  1. ubuntu 12.04下 eclipse的安装
  2. mybaits trim用法
  3. mac 设置mysql开机自启动
  4. .NET中的UI自动化测试
  5. C++之private虚函数
  6. 秋季唯美海报,打造的一系列秋季主题视觉
  7. nccloud开发环境搭建_VS Code 搭建开发环境
  8. 如何让普通用户执行一些root用户才能执行的命令
  9. keeplive+haproxy+nginx
  10. Anaconda安装python模块
  11. Java用while求100以内奇数和
  12. error A2070:invalid instruction operands 错误原因
  13. 笔记本怎么自己装系统?u盘装系统windows7教程图解
  14. 有趣的MATLAB小程序
  15. 江苏大专计算机考试,江苏省教育考试院2020年高职(专科)注册入学申请入口...
  16. python中的strip()方法
  17. 免费小程序开发平台有哪些功能对零售超市行业有利?
  18. 配置网络接口的“IP“命令
  19. 云原生 · DevOps`01 | 光速初识DevOps
  20. 聊聊那些令人愉悦的动画特效(GIF图)

热门文章

  1. 什么是股票分仓软件, 实现原理解析2
  2. 小白零基础C#学习笔记
  3. 常见库爬取58二手全站信息
  4. Launcher进程启动流程
  5. C/C++ 数据范围int
  6. 宽带信号doa matlab,宽带信号DOA估计处理方法研究
  7. html制作古诗带图画大全,春天的古诗配图画大全
  8. GA遗传算法实现记录 C++版本 解决多元函数最值问题
  9. 三年java经验面试总结,整理了一些java面试题供参考
  10. DVWA测试XSS跨站脚本攻击三种类型