Java泛型的协变与逆变
泛型擦除
Java的泛型本质上不是真正的泛型,而是利用了类型擦除(type erasure),比如下面的代码就会出现错误:

报的错误是:both methods have same erasure
原因是java在编译的时候会把泛型,上面的<String>和<Integer>都给擦除掉(其实并没有真正的被擦除,javap -l -p -v -c可以看到LocalVariableTypeTable
里面有方法参数类型的签名)。
协变与逆变
理解了类型擦除有助于我们理解泛型的协变与逆变,现有几个类如下:
Plant Fruit Apple Banana Orange
其中Apple、Banana、Orange是Fruit的子类,Fruit是Plant的子类。我们来看下下面的代码:

这里有些同学可能不明白,为什么编译会报错呢?ArrayList是List的子类,Apple是Fruit的子类,那么我这里的泛型转换为什么出问题呢?
因为泛型没有内建的协变类型,无法将List<Fruit>和ArrayList<Apple>关联起来,所以在编译阶段就会出现错误。
协变
于是我们可以利用通配符实现泛型的协变:<? extends T>子类通配符;这个通配符定义了?继承自T,可以帮助我们实现向上转换:

看起来很美好吧,其实不然。这里我们要理解当转换之后list中的数据类型是什么。虽然将Apple类型赋值给了list,但是list的类型是? extends Fruit,
把? extends Fruit看成一个整体,我们能确定list的具体类型肯定是Fruit或者Fruit的父类(因为一个类只能有一个直接父类,所以确定了Fruit,那么Fruit的父类
则都是可以确定的),而不能确定list的类型是Fruit的子类当中具体的哪一个?(有多个类都继承自Fruit),所以这也就直接导致了一旦使用了<? extends T>
向上转换之后,不能再向list中添加任何类型的对象了,这个时候只能选择从list当中get数据而不能add。

另外还需要注意的是,这个时候从list当中get出来的数据不再是Apple,而是Fruit或者Fruit的父类:

逆变
逆变则和协变相反,它是向下转换:

逆变使用通配符? super T(超类通配符),如上面代码,Fruit是Apple的超类,则这个时候对于JVM来说,它能确定list的类型的超类肯定是Apple
或者Apple的父类,换言之该类型就是Apple或者Apple的子类,所以和上面的协变一样,既然确定了类型的范围,那么list能够add的类型也就是Apple或者Apple的子类了。
从逆变和协变的描述中我们可以总结一下:协变用于下转上,转换之后不能再添加任何类型;逆变用于上转下。
具体什么使用使用协变,什么时候使用逆变,可以参考这篇文章:
其中提到PECS(producer-extends, consumer-super)。比如list.get(0)这种,list作为数据源producer;而list.add(new Apple()),list作为数据处理端consumer。
本文结束!
Java泛型的协变与逆变的更多相关文章
- Java用通配符 获得泛型的协变和逆变
Java对应泛型的协变和逆变
- Kotlin泛型与协变及逆变原理剖析
在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...
- .NET 4.0中的泛型的协变和逆变
转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...
- 转载.NET 4.0中的泛型的协变和逆变
先做点准备工作,定义两个类:Animal类和其子类Dog类,一个泛型接口IMyInterface<T>, 他们的定义如下: public class Animal { } public ...
- Java中的协变与逆变
Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- C# 泛型的协变和逆变 (转载)
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量. 协变和逆变是两个相互对立的概念: ...
- C#-弄懂泛型和协变、逆变
脑图概览 泛型声明和使用 协变和逆变 <C#权威指南>上在委托篇中这样定义: 协变:委托方法的返回值类型直接或者间接地继承自委托前面的返回值类型; 逆变:委托签名中的参数类型继承自委托方法 ...
随机推荐
- python3笔记-列表
列表去重的两种方式: # 创建列表放数据 a =[1,2,1,4,2] b=[1,3,4,3,1,3] d=[] for i in a: if i not in d: d.append(i) prin ...
- 利用阿里云服务器免费体验word press博客、个人网站
本文首发于我的个人博客:https://chens.life/create-wordpress-blog.html 前言 目前市面上有许许多多的虚拟云服务器ECS,例如阿里云.华为云.又拍云等等,他们 ...
- AP、AC、无线路由器
起因 AP.AC.无线路由器 一直都傻傻的分不清,今天就好好的研究一下他们之间到底有什么联系和区别~ AP 什么是AP? 无线AP(Access Point):即无线接入点,它用于无线网络的无线交换机 ...
- .NET Core 下使用 Apollo 配置中心
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理场景.服务 ...
- css3 压缩及验证工具
1.css w3c统一验证工具 网址:http://www.csstats.com/ 如果你想要更全面的,这个神奇,你值得拥有: w3c统一验证工具:http://validator.w3.org/u ...
- Java语言的优势
首先, Java语言是一种纯粹的面向对象的编程语言.这样就决定了Java语言更能直接客观地反映现实生活中的对象,因此Java语言更适合大型的复杂系统开发. 其次, Java语言是一种平台无关的语言. ...
- react项目结合echarts,百度地图实现热力图
一.最近在一个react项目(antd pro)中需要展示一个热力地图.需求是: 1.热力地图可缩放: 2.鼠标点击可以展示该点地理坐标,及热力值. 3.初始化时候自适应展示所有的热力点. 4.展示热 ...
- IHttpClientFactory组件使用
起因 :由于需要前段时间写了一个http请求的公共方法 使用的 HttpClient,但是在jmeter测试下 爆发了这个问题:“Cannot assign requested address Ca ...
- 关于JSON的零碎小知识
1.ali的fastjson在将实体类转成jsonString的时候,一些首字母大写的字段会自动修改为小字母,这种字段加 @JsonProperty(value = "DL_id" ...
- java虚拟机小贴士之GC分析
打印日志 通过加入 -XX:+PrintGCDetails 参数则可以打印详细GC信息至控制台.参数-verbose:gc也是可以,但不够详细.通过加入-XX:+PrintGCDateStamps则可 ...