一、类型擦除(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. PowerDesigner导入Excel/CSV
  2. Trouble Sort CodeForces - 1365B(思维)
  3. Elasticsearch之需要注意的问题(es和jdk版本)
  4. Moss2007搜索服务配置,没有索引器和搜索配置页面报错问题解决
  5. python 输出 GPU内存 最大使用率
  6. github private链接访问_如何将Jenkins链接到私有Github存储库?
  7. ios 蓝牙命令发送_实战恢复cisco 2950交换机的IOS
  8. linux traceroute命令详解_详解Linux系统路由跟踪指令traceroute语法、工作原理和实例说明...
  9. Java基础学习总结(119)——Java8 JVM与Java7 JVM比较
  10. Spring的IOC理解(转载)
  11. Ubuntu 网速显示,ssh配置
  12. 19.mysql 字符集和校对规则
  13. 94. autoload
  14. new delete 来申请动态二维数组空间
  15. [转载] 3 idiots
  16. Arcmap实现航线按日期分段
  17. 使用css实现产品分类,DIV+CSS实现京东商城分类适合所有版本
  18. 怎么使用openssl来生成一个自签名的x509证书?
  19. Cisco Packet Tracer Student 两个路由器级联
  20. 拉普拉斯变换卷积法处理非齐次线性微分方程通解

热门文章

  1. Flask框架中url_for用法详解
  2. linux下鼠标主题以及gtk主题,图标主题,字体的设置
  3. ESP32-硬件SPI读取MCP3208
  4. 数位DP POJ3208 Apocalypse Someday
  5. Python崛起:“人生苦短,我用Python”并非一句戏言
  6. Unittest简单项目
  7. Origin 中对曲线打定点,以及显示相应的坐标轴刻度
  8. dubbo源码学习三—暴露服务exporter、invoker
  9. C++双人战争游戏(机房娱乐)
  10. Spring + Maven + Dubbo + MyBatis + Linner + Handlebars-Web开发环境搭建