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#权威指南>上在委托篇中这样定义: 协变:委托方法的返回值类型直接或者间接地继承自委托前面的返回值类型; 逆变:委托签名中的参数类型继承自委托方法 ...
随机推荐
- 【python】超有用的集合类collections,不来了解一下?
在使用Python的过程中,一定是离不开数据结构的, 也就是List-列表,Tuples-元组,Dictionaries-字典. 那实际应用中我们更多的还是要去操作这些结构里的数据.比如,在列表后面添 ...
- 跟我一起学.NetCore之中间件(Middleware)应用和自定义
前言 Asp.NetCore中的请求管道是通过一系列的中间件组成的,使得请求会根据需求进行对应的过滤和加工处理.在平时开发中会时常引用别人定义好的中间件,只需简单进行app.Usexxx就能完成中间件 ...
- 网站远程附件存储到 OSS
参考:链接 链接 链接 简介 网站远程附件功能是指将用户上传的附件直接存储到远端的存储服务器,一般是通过FTP的方式存储到远程的FTP服务器,将论坛附件保存在 OSS 上有以下好处: 附件将拥有更 ...
- window.location.href跳转无效
window.location.href跳转无效 问题情况 JS中设置window.location.href跳转无效 原因是 a标签的href跳转会执行在window.location. ...
- Python实践项目2
#南昌理工学院人工智能学院实验室WORKSHOP实践项目 import time import random SCRIPT_NPC_SCHOOL_SISTER = ['你好!', '你好!', '你是 ...
- AI研讨会直播:《人工智能开发前沿》实战系列公开课第1期
报名链接:https://www.slidestalk.com/m/276 活动背景 业务需求.数据.算法.算力等因素,决定人工智能技术走向产业落地面临各种挑战.博客园联合示说网以及产业内人工智能技术 ...
- ckeditor4.0以上使用行间距插件lineheight报错修改
①从百度上下载一个 ckeditor 行距包,解压放到ckeditor/plugins目录下. ②在config.js 中添加 config.extraPlugins += (config.extra ...
- PHP生成二维码 endroid/qr-code 扩展包
目录 1. 引入 endroid/qr-code 扩展包 2. 使用示例 3. 二维码中放入logo,无法识别二维码的问题 1. 引入 endroid/qr-code 扩展包 https://pack ...
- 关于java基础_数组的学习
数组的学习 1.数组的概念?作用是什么? 系统中存储多个值, 2.数组的定义? 数据类型[] 数组名; 3.定义好数组以后需要对其进行初始化 数组初始化有两种: 第一种动态初始化,指定数组的长度,长度 ...
- Linux磁盘管理及LVM讲解(1)
硬盘接口 从整体的角度上,硬盘接口分为IDE.SATA.SCSI和SAS四种,IDE接口硬盘多用于家用产品中,也部分应用于服务器,SCSI接口的硬盘则主要应用于服务器市场,而SAS只在高端服务器上,价 ...