java 泛型 协变_泛型的逆变与协变
一、类型擦除(Type Erasure)
Java的泛型本质上不是真正的泛型,而是利用了类型擦除,比如下面的代码就会出现错误:
报的错误是:both methods have same erasure
原因是Java在编译的时候会把泛型,上面的和都给擦除掉。Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。
PS:其实并没有真正的被擦除,javap -l -p -v -c可以看到LocalVariableTypeTable里面有方法参数类型的签名。
二、不变(invariant)
理解了类型擦除有助于我们理解泛型的协变与逆变,现有几个类如下:
Plant Fruit Apple Banana Orange
其中Apple、Banana、Orange是Fruit的子类,Fruit是Plant的子类。我们来看下下面的代码:
这里有些同学可能不明白,为什么编译会报错呢?ArrayList是List的子类,Apple是Fruit的子类,那么我这里的泛型转换为什么出问题呢?
因为泛型没有内建的协变类型,无法将List和ArrayList关联起来,所以在编译阶段就会出现错误。
三、协变(covariant)
于是我们可以利用通配符实现泛型的协变: extends T>子类通配符;这个通配符定义了?继承自T,可以帮助我们实现向上转换:
List extends Fruit> list = new ArrayList();
看起来很美好吧,其实不然。这里我们要理解当转换之后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的父类:
四、逆变(contravariant)
逆变则和协变相反,它是向下转换:
List super Apple> list = new ArrayList();
此时增加一个Apple的子类是BigApple,然后往list里去添加,结果如下:
逆变使用通配符 super T>(超类通配符),如上面代码,Fruit是Apple的超类,则这个时候对于JVM来说,它能确定list的类型的超类肯定是Apple或者Apple的父类,换言之该类型就是Apple或者Apple的子类,所以和上面的协变一样,既然确定了类型的范围,那么list能够add的类型也就是Apple或者Apple的子类了。
总结一下,便于记忆:
协变:extends/向上转换/不能add/只能get(T及父类)
逆变:super/向下转换/不能get/只能add(T及子类)
五、PECS(producer-extends, consumer-super)
如果参数化类型表示一个生产者,就使用 extends T>。比如list.get(0)这种,list作为数据源producer;
如果它表示一个消费者,就使用 super T>。比如:list.add(new Apple()),list作为数据处理端consumer。
参考的原文链接,感谢作者!
如有错误,请更正指出,谢谢!
java 泛型 协变_泛型的逆变与协变相关推荐
- C#高级语法之泛型、泛型约束,类型安全、逆变和协变(思想原理)
一.为什么使用泛型? 泛型其实就是一个不确定的类型,可以用在类和方法上,泛型在声明期间没有明确的定义类型,编译完成之后会生成一个占位符,只有在调用者调用时,传入指定的类型,才会用确切的类型将占位符替换 ...
- 跟着小老弟来学习Kotlin中的逆变和协变
/ 今日科技快讯 / 近日,小米创始人.董事长兼CEO雷军在抖音上开启了其直播带货的首秀.从晚上8点开播,到晚上10点,销售额就已经破亿.包括1000台售价49999元的透明电视在内的商品一推 ...
- 第五节:泛型(泛型类、接口、方法、委托、泛型约束、泛型缓存、逆变和协变)
一. 泛型诞生的背景 在介绍背景之前,先来看一个案例,要求:分别输出实体model1.model2.model3的id和name值,这三个实体有相同的属性名字id和name. 1 public cla ...
- 泛型委托的逆变和协变
泛型委托:委托实际只是提供了4个方法的一个类定义.这4个方法包括:一个构造器.一个Invoke方法.一个BeginInvoke方法和一个EndInvoke方法.如果定义的一个委托类型指定了类型参数,编 ...
- java中的逆变、协变、不变概念讲解转载自http://www.cnblogs.com/en-heng/p/5041124.html,感谢编程路上的前辈们!
En-Heng 无他,但手熟尔 博客园 首页 新随笔 联系 订阅 管理 随笔 - 32 文章 - 0 评论 - 33 Java中的逆变与协变 看下面一段代码 Number num = new In ...
- Java中的逆变与协变
学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java! 协变和逆变指的是宽类型和窄类型在某种情况下 ...
- 协变逆变java_Java中的逆变与协变
什么是逆变与协变 协变(Covariance) 如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变 逆变(Contravariance) 如果B是A的子类,并且F(B)成了F(A)的父类 ...
- 【转】逆变与协变详解
逆变(contravariant)与协变(covariant)是C#4新增的概念,许多书籍和博客都有讲解,我觉得都没有把它们讲清楚,搞明白了它们,可以更准确地去定义泛型委托和接口,这里我尝试画图详细解 ...
- ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
一:如何去学习?都去学习什么? 1:学习优秀项目的设计思想,多问几个为什么,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中 2:学习优秀项目的代码风格,代码的封装设计 ...
最新文章
- PowerDesigner导入Excel/CSV
- Trouble Sort CodeForces - 1365B(思维)
- Elasticsearch之需要注意的问题(es和jdk版本)
- Moss2007搜索服务配置,没有索引器和搜索配置页面报错问题解决
- python 输出 GPU内存 最大使用率
- github private链接访问_如何将Jenkins链接到私有Github存储库?
- ios 蓝牙命令发送_实战恢复cisco 2950交换机的IOS
- linux traceroute命令详解_详解Linux系统路由跟踪指令traceroute语法、工作原理和实例说明...
- Java基础学习总结(119)——Java8 JVM与Java7 JVM比较
- Spring的IOC理解(转载)
- Ubuntu 网速显示,ssh配置
- 19.mysql 字符集和校对规则
- 94. autoload
- new delete 来申请动态二维数组空间
- [转载] 3 idiots
- Arcmap实现航线按日期分段
- 使用css实现产品分类,DIV+CSS实现京东商城分类适合所有版本
- 怎么使用openssl来生成一个自签名的x509证书?
- Cisco Packet Tracer Student 两个路由器级联
- 拉普拉斯变换卷积法处理非齐次线性微分方程通解
热门文章
- Flask框架中url_for用法详解
- linux下鼠标主题以及gtk主题,图标主题,字体的设置
- ESP32-硬件SPI读取MCP3208
- 数位DP POJ3208 Apocalypse Someday
- Python崛起:“人生苦短,我用Python”并非一句戏言
- Unittest简单项目
- Origin 中对曲线打定点,以及显示相应的坐标轴刻度
- dubbo源码学习三—暴露服务exporter、invoker
- C++双人战争游戏(机房娱乐)
- Spring + Maven + Dubbo + MyBatis + Linner + Handlebars-Web开发环境搭建