一、类型擦除(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 泛型 协变_泛型的逆变与协变相关推荐

  1. C#高级语法之泛型、泛型约束,类型安全、逆变和协变(思想原理)

    一.为什么使用泛型? 泛型其实就是一个不确定的类型,可以用在类和方法上,泛型在声明期间没有明确的定义类型,编译完成之后会生成一个占位符,只有在调用者调用时,传入指定的类型,才会用确切的类型将占位符替换 ...

  2. 跟着小老弟来学习Kotlin中的逆变和协变

    /   今日科技快讯   / 近日,小米创始人.董事长兼CEO雷军在抖音上开启了其直播带货的首秀.从晚上8点开播,到晚上10点,销售额就已经破亿.包括1000台售价49999元的透明电视在内的商品一推 ...

  3. 第五节:泛型(泛型类、接口、方法、委托、泛型约束、泛型缓存、逆变和协变)

    一. 泛型诞生的背景 在介绍背景之前,先来看一个案例,要求:分别输出实体model1.model2.model3的id和name值,这三个实体有相同的属性名字id和name. 1 public cla ...

  4. 泛型委托的逆变和协变

    泛型委托:委托实际只是提供了4个方法的一个类定义.这4个方法包括:一个构造器.一个Invoke方法.一个BeginInvoke方法和一个EndInvoke方法.如果定义的一个委托类型指定了类型参数,编 ...

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

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

  6. Java中的逆变与协变

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

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

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

  8. 【转】逆变与协变详解

    逆变(contravariant)与协变(covariant)是C#4新增的概念,许多书籍和博客都有讲解,我觉得都没有把它们讲清楚,搞明白了它们,可以更准确地去定义泛型委托和接口,这里我尝试画图详细解 ...

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

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

最新文章

  1. AI做不了“真”3D图像?试试Google的新生成模型
  2. Linux中命令链接操作符的十个最佳实例
  3. springmvc数据验证
  4. Spring基础知识和配置
  5. Meteor环境安装配置
  6. 地图标绘软件_ArcGIS Pro 2.4 首次亮相,Indoors、Monitor、全新一代地图故事模板,新特性抢先看!...
  7. [CareerCup] 8.7 Chat Server 聊天服务器
  8. Win11系统Windows更新疑难解答出现问题怎么办?
  9. Python菜鸟之路:原生Ajax/Jquery Ajax/IFrame Ajax的选用
  10. kitti数据集介绍论文Vision meets Robotics: The KITTI Dataset
  11. python面向对象
  12. qt怎么连接oracle,Qt连接Oracle数据库详细介绍(QOCI)
  13. matlab 多项式画图,matlab多项式画图
  14. HDOJ Problem - 1299
  15. 什么是MIT开源协议
  16. 零知开发板和红外人体感应模块使用
  17. 计算机组成原理语言方框图,计算机组成原理3---方框图语言
  18. 画画初学者怎么画好素描?画素描的技巧有哪些?
  19. 什么是SSH 以及常见的ssh 功能
  20. 中国信保总经理唐若疑卷入ST宏盛合同诈骗案昕下马

热门文章

  1. UWB(Ultra Wideband)
  2. Oracle基础学习四之表的定义及脚本编写一
  3. 【MATLAB】求算凸轮摆杆机构的最大压力角和最小半径并画出凸轮轮廓
  4. 【创新应用】5AloT(5G+AI+物联网)深度研究报告:下一轮科技红利
  5. 房地产行业现状 房地产龙头股
  6. 动态磁盘导致无法进入Mac系统的解决办法
  7. PBOOTCMSV3.1.3火车头采集器7.6免登录 新闻文章发布
  8. Bartender 4 for Mac(菜单栏应用图标管理)
  9. Ncurses学习经历(十三)菜单系统的核心
  10. DokuWiki基本使用语法