本文转载自微信公众号《咸鱼翻身》,作者MDove。转载本文请联系闲鱼正翻车公众号。前言日常琐碎的时间不适合看一些长篇优质的文章,但琐碎的时间也是时间,看一些短小精悍的文章查漏补缺也是极好的。让“碎片文章”填满碎片时间。今天“碎片化文章”的主题:泛型——逆变和协变。求逆和协方差放在这里。估计很多朋友都会上当受骗。毕竟,我们在日常生活中似乎并没有太多接触过这个概念。不是这种情况。我们在日常开发中经常会看到它,只是不知道这样一个名词。文字OK,今天5分钟的短文,我们来聊聊逆和协方差的概念。1.基本概念其实两者的概念都很容易理解。接下来,我们仔细阅读下面这段话:逆变和协变是用来描述类型转换后的继承关系的。如果A和B表示类型,f(...)表示类型转换,≤表示继承关系(比如A≤B表示A是B的子类)如果f(...)是逆变的,则当A≤B,则f(B)≤f(A)成立。如果f(...)是协变的,那么当A≤B时,则f(A)≤f(B)成立。额外补充一句:如果f(...)为常量,则当A≤B时,则f(B)与f(A)无关2.代码场景如果你充分理解了上面的内容,你其实可以想想我们日常代码中的例子:数组是协变的一种;泛型是不可变的。上面的代码:publicclassAextendsB{}publicclassB{}publicvoidtest(){B[]arrs=newA[66];Listlist=newArrayList();}这段代码不能编译:因为数组是协变的,所以A[]是B[]的子类;而泛型不是,所以List不是List的子类。3.通配符的含义因为这个原因,才有了通配符。3.1.Covariance-upperlimitwildcard代码改成这样后可以正常编译:通配符的存在使得泛型产生协变性,使得List成为List的子类。但是我估计有经验的同学已经知道这样做是“没用的”了,因为:我们发现原来是这么干的。对于列表变量,我们只能get()而不能add()!一时接受不了?其实这里很好理解。协变之后,对于list,我可以指向List的很多子类。假设这个时候我们可以随意add(),那对于runtime来说就是一场灾难:因为我可以随意add(newA());add(newC())。如果存在这种情况,那我在get()的时候,是不是只能把它当作B来用,因为这里可能有A或者C...这样做是完全没有意义的...所以就有了下面的内容:反转-lowerlimitwildcards3.2,Inversion-lowerlimitwildcards直接上代码:publicclassAextendsB{}publicclassB{}publicclassCextendsB{}publicclassDextendsA{}publicvoidtest2(Listlist){list.add(newA());list.add(newB());list.add(newC());list.add(newD());}此时我们会发现:我们可以add(),A及其子类。而这种实现是从我们的反转概念中诞生的。3.3.小思考仔细想想就会发现,这些都是开发阶段或者编译阶段的限制。做这么多限制的目的是什么?或者有什么好处?这部分的讨论我们以后再说。到最后,我想说的内容就结束了。关于泛型的话题还有很多,熟练使用和理解泛型是我们编写工具和框架的关键帮助。
