什么是逆变与协变

协变(Covariance)

如果B是A的子类,并且F(B)也是F(A)的子类,那么F即为协变

逆变(Contravariance)

如果B是A的子类,并且F(B)成了F(A)的父类,那么F即为逆变

Java中的逆变与协变

Java中的泛型有逆变和协变两种操作,定义如下:

协变

extends A>

B是A的子类,那么List< B >是List extends A>的子类

逆变

supper A>

B是A的子类,那么List< B >是List super A>的父类

Java中逆变与协变的约束

Java的协变逆变及其约束,都是出于对多态的应用。为了后续说明方便,这里先定义一系列的父子类

class Fruit {

public String returnMeat() {

return "generic fruit meat";

}

}

class Apple extends Fruit {

@Override

public String returnMeat() {

return "apple meat";

}

}

class GreenApple extends Apple {

@Override

public String returnMeat() {

return "green apple meat";

}

}

多态

Java是支持多态的。如果一个方法的参数接收的是A类型,那么将其子类型作为参数,调用该方法,依然可行。

例如eatFruitMeat方法就能体现多态特性

@Test

public void test1() {

eatFruitMeat(new Fruit());//输出eat generic fruit meat

eatFruitMeat(new Apple());//输出eat apple meat

eatFruitMeat(new GreenApple());//输出eat green apple meat

}

public void eatFruitMeat(Fruit fruit) {

System.out.println("eat "+fruit.returnMeat());

}

协变约束

协变方法支持对传入参数的读操作,但不支持修改操作。如下:

@Test

public void test1() {

List greenApples = Lists.newArrayList(new GreenApple());

List fruits = Lists.newArrayList(new Fruit());

List apples = Lists.newArrayList(new Apple());

eatFruitMeats(greenApples);

eatFruitMeats(fruits);//编译错误1

eatFruitMeats(apples);

}

public void eatFruitMeats(List extends Apple> fruits) {

fruits.forEach(fruit->System.out.println("eat "+fruit.returnMeat()));

fruits.add(new Apple());//编译错误2

fruits.add(new Fruit());//编译错误3

fruits.add(new Object());//编译错误4

}

编译错误1: eatFruitMeats方法接受的List extends Apple>的子类,显然List不是其子类

编译错误2,3,4: eatFruitMeats方法在被调用前,并不知道最终调用方,传递进来的具体是哪一个子类?有可能是List< Apple >,也有可能是List< GreenApple >,所以贸然向其中添加任何对象,都是可能出错,比如你不能把一个Apple对象放进List< GreenApple >。为了防止这些可能的错误,编译器提前进行了约束限制。

逆变约束

逆变主要在写的场景,即只能向逆变容器中添加,下界类型本身或其子类

@Test

public void test1() {

List fruits = Lists.newArrayList();

List apples = Lists.newArrayList();

List greenAppleLists = Lists.newArrayList();

collectFruits(fruits);

collectFruits(apples);

collectFruits(greenAppleLists);//编译错误1

}

public void collectFruits(List super Apple> fruits) {

fruits.add(new Fruit());//编译错误2

fruits.add(new Apple());

fruits.add(new GreenApple());

}

编译错误1: 由于是逆变,所以List是List super Apple>的父类。所以不能将greenAppleLists作为参数调用collectFruits方法,因为不满足 Java 方法参数的多态性要求,即只能传本类或子类的要求

编译错误2: 如果调用方传递的是List< Apple >,那往其中添加父类Fruit对象,在运行时肯定会报错,为了避免这种情况,编译器提前报错。

总结

Java泛型支持协变和逆变,具体在使用时,会有一些约束。这些约束,需要从Java语言的特性,比如多态性,以及运行时安全性去理解。

简单总结协变、逆变参数的方法调用特点如下:

协变参数

只接受自己的子类。协变的父子关系,同类原本的父子关系一致。如GreenApple是Apple的子类,List< GreenApple >是List extends Apple>的子类

对写有约束,只能用于读

逆变参数

只接受自己的子类。逆变的父子关系,同类原本父子关系相反。如GreenApple是Apple的子类,List< GreenApple >是List super Apple>的父类

只能写入下界的子类,本例中,只能向List中写入Apple及Apple的子类

参考链接

协变逆变java_Java中的逆变与协变相关推荐

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

    Java作为面向对象的典型语言,相比于C++而言,对类的继承和派生有着更简洁的设计(比如单根继承). 在继承派生的过程中,是符合Liskov替换原则(LSP)的.LSP总结起来,就一句话: 所有引用基 ...

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

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

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

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

  4. 工频风力发电储能逆变电路中的电流检测(霍尔电流传感器ACS712/CH701应用案例)

    在传统的风力发电中,由于风力的不稳定性,因此直接产生的电压不稳定,不能直接被利用,需要进一步的稳压,储能,转化才能实现正常的使用. 为了克服传统的风力发电问题,本文介绍了一种工频风力发电储能逆变电路, ...

  5. UPS电源中的逆变电路与Simulink仿真

    一.设计题目: UPS电源中的逆变电路 主要指标及要求: 输入电压240V 输出电压220/230/240V 效率>90% 二.设计思路.方案选择及电路工作原理: 采用单相全桥逆变电路,并使用双 ...

  6. 倒序存放数组java_java实现数组中的逆序对

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对,例如在数组{7,5,6,4}中,一共存在5对逆序对,分别是{7,6},{7,5},{7,4},{6,4},{5,4}.输 ...

  7. 教你如何攻克Kotlin中泛型型变的难点(下篇)

    简述: 前几天我们一起为Kotlin中的泛型型变做了一个很好的铺垫,深入分析下类型和类,子类型和子类之间的关系.什么是子类型化关系以及型变存在的意义.那么今天将会讲点更刺激的东西,也就是Kotlin泛 ...

  8. 教你如何攻克Kotlin中泛型型变的难点(应用篇)

    简述: 这是泛型型变最后一篇文章了,也是泛型介绍的最后一篇文章.顺便再扯点别的,上周去北京参加了JetBrains 2018开发者日,主要是参加Kotlin专场.个人感觉收获还是挺多的,bennyHu ...

  9. 面试题36:数组中的逆序对

    题目:在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数.例如,有一个数组为Array[0..n] 其中有元素a[i],a[j]. ...

最新文章

  1. Keras入门(一)搭建深度神经网络(DNN)解决多分类问题 1
  2. c#中的interface abstract 与 virtual(转)
  3. 计算机英语讲课笔记03
  4. opencv 伽马变换
  5. python包标准类型和动态导入模块和多态和反射和授权
  6. 线性同余算法 (LCG)
  7. 格雷码与二进制相互转换的verilog实现
  8. 安科瑞配电室综合监控系统实现配电室内环境的在线监测,保障配电室设备的安全运行
  9. 回归分析——简单线性回归实例讲解(SPSS)
  10. Ordering disordered structures
  11. 电脑复制,电脑复制粘贴,详细教您电脑不能复制粘贴怎么办
  12. Proxmox监视器
  13. matlab读多个excel,读取excel 多个表格文件数据-怎样利用matlab去读取一个excel表中多个sheet的数......
  14. WINVNC(二)omni_thread
  15. 记录通过的阿里云认证
  16. 四通畜牧数据库使用说明
  17. Java if判断,while判断,Do while判断,Switch判断
  18. 雷达原理习题【西电】
  19. 1. 无法解析的外部符号 “__declspec(dllimport) const XXX::vftable“ ` 2. `无法定义 dllimport 实体`
  20. 对话黄骁俭:SAP的工程师文化

热门文章

  1. BQ24296充电管理芯片使用过程中的注意事项
  2. windows10 IOT +Azure会议概要总结
  3. 容器日志采集利器Log-Pilot
  4. H5活动刮刮卡功能的实现与注意事项
  5. 第一章---对象导论
  6. mysql配置文件调优
  7. winpcap 发送数据包
  8. Axis,axis2,Xfire以及cxf对比 (转)
  9. PHP MySql数据库访问
  10. 【AwayPhysics学习笔记】:Away3D物理引擎的简介与使用