写在前面


  • 和小伙伴分享一些java小知识点,主要围绕下面几点:
  • 什么是逆变(contravariant)&协变(covariant)?
  • 数组支持协变&逆变吗?
  • 泛型支持协变&逆变吗?
  • 部分内容参考
    • 《编写高质量代码(改善Java程序的151个建议)》
  • 博文理解有误的地方小伙伴留言私信一起讨论

生活不能等待别人来安排,要自己去争取和奋斗;而不论其结果是喜是悲,但可以慰藉的是,你总不枉在这世界上活了一场。有了这样的认识,你就会珍重生活,而不会玩世不恭;同时,也会给人自身注入一种强大的内在力量。 ——路遥《平凡的世界》


关于协变逆变到底是什么意思,其实很好理解,用一句话描述:(小伙伴们看到下面的话,会不会想到这不就是多态吗,哈,今天我们只看协变和逆变,关于多态的一些内容,如强制多态包含多态重载多态等之后有机会和小伙伴们分享)

协变窄类型替换宽类型逆变宽类型覆盖窄类型

这里的窄类型子类(派生类),这里的宽类型父类(基类,超类),那这里的替换覆盖又是什么意思,这里就要说到OO(面相对象)六大设计原则之一的LSP(里氏代换原则 Liskov Substitution Principle),里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为

下面们结合代码就数组和泛型的协变和逆变进行分析

我们来看一段代码

package com.liruilong;import java.util.Arrays;/*** @Project_name: workspack* @Package: com.liruilong* @Description:* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/11  1:18*/
public class CovariantDemo {public static void main(String[] args) {Number [] numbers = {1,1L,3d,2.0F};Arrays.stream(numbers).forEach(System.out::print);}
}

Number类是所有基本类型封装类的父类,同理基本类型封装类为Number类的子类,关于自动装箱和自动拆箱是java在JDK1.5的时候引入的新特性,我们这里不多讲,上面的代码可以正常编译,并且输出下面的内容,这里,数组里的基本类型装箱为封装类放到了堆中,这些封装类可以出现在Number类定义的数组中,说明子类可以替换了父类,即数组是满足协变的。

113.02.0
Process finished with exit code 0

既然数组支持协变,那么逆变呢?我们来看看

package com.liruilong;import java.util.Arrays;/*** @Project_name: workspack* @Package: com.liruilong* @Description:* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/11  1:18*/
public class CovariantDemo {public static void main(String[] args) {Number [] numbers = {new Object()};Arrays.stream(numbers).forEach(System.out::print);}
}

这里我们把数组元素换成Object类,即所有类的父类,希望是可以通过父类来覆盖代替子类,但是直接编译报错,说明数组不支持直接逆变

Error:(17, 30) java: 不兼容的类型: java.lang.Object无法转换为java.lang.Number

数组不支持直接逆变,那么是否可以接见的实现逆变的,这里我么就要用到多态里的一种,强制多态,即强制类型转化试试

package com.liruilong;import java.util.Arrays;/*** @Project_name: workspack* @Package: com.liruilong* @Description:* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/11  1:18*/
public class CovariantDemo {class A {}class B extends A{}public static void main(String[] args) {A a = new CovariantDemo().new A();B [] bs = {(B) a};Arrays.stream(bs).forEach(System.out::print);}
}

类型转化报错。说明对于数组的逆变来讲,是不支持逆变的,将父类强制转化为子类报类型转化异常,java并没有对这方面做限制。

Exception in thread "main" java.lang.ClassCastException: com.liruilong.CovariantDemo$A cannot be cast to com.liruilong.CovariantDemo$Bat com.liruilong.CovariantDemo.main(CovariantDemo.java:24)Process finished with exit code 1

通过上面代码,我们可以知道数组支持协变,不支持逆变,那泛型呢?对于协变和逆变是否支持

泛型不支持协变也不支持逆变,即不能把一个父类对象赋值给一个子类类型变量,相反也是同理。

下面我们看看代码

package com.liruilong;import java.util.ArrayList;
import java.util.List;/*** @Project_name: workspack* @Package: com.liruilong* @Description:* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/11  1:18*/
public class CovariantDemo {public static void main(String[] args) {List<Number> ln = new ArrayList<Integer>();}
}

java为了保证运行期安全性,必须保证泛型参数类型固定的,所以它不允许一个泛型参数可以同时包含两种类型,即使为父子关系也不行。所以直接编译报错,即泛型不支持协变也不支持逆变.

Error:(17, 27) java: 不兼容的类型: java.util.ArrayList<java.lang.Integer>无法转换为java.util.List<java.lang.Number>

但可以使用通配符(Wildcard)模拟协变逆变,通配符在编译期有效,在运行期必须为一个明确的类型

package com.liruilong;import java.util.ArrayList;
import java.util.List;/*** @Project_name: workspack* @Package: com.liruilong* @Description:* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/11  1:18*/
public class CovariantDemo {public static void main(String[] args) {List< ? extends Number > list = new ArrayList<Integer>();}
}

Number的子类型都可以为泛型类型参数,即允许NUmber所有的子类作为泛型参数类型,在运行期为一个具体的值.编译没有报错


Process finished with exit code 0

逆变同样也是可以,即泛型可以通过superextends来模拟实现协变和逆变,但是本身是不存在协变和逆变的,这里主要利用了泛型在编译器有效

List< ? super Integer> li = new ArrayList<Number>();

关于协变逆变就和小伙伴分享到这里,嗯,还有协变逆变方法,这里要简单说明下

协变方法: 即子类的方法返回值的类型比父类方法要窄,即该方法为协变方法,也称多态,覆写,重写

//子类的doStuff()方法返回值的类型比父类方法要窄,即该方法为协变方法,也称多态。class A{public  Number doStuff(){return 0;}}class B extends A{@Overridepublic  Integer doStuff(){return 0;}}

逆变方法:子类的方法返回值的类型比父类方法宽,此时为逆变方法。虽然子类扩大了父类的输入返回参数,但是这里已经是重载了。

//子类的doSutff方法返回值的类型比父类方法宽,此时为逆变方法,class C {public Integer doStuff(Integer i) {return 0;}}class D extends C {public Number doStuff(Number i) {return 0;}}

[JAVA冷知识]什么是逆变(contravariant)与协变(covariant)?数组是否支持协变逆变?泛型呢?相关推荐

  1. [JAVA冷知识]JAVA居然支持多继承?让我们用内部类去实现吧

    写在前面 JAVA冷知识,今天和小伙伴分享的是 通过内部类的方式实现JAVA的多继承 一个Demo和JDK源码中的具体场景 部分内容参考 <编写高质量代码(改善Java程序的151个建议)> ...

  2. [JAVA冷知识]动态加载不适合数组类?那如何动态加载一个数组类?

    写在前面 今天和小伙伴分享一些java小知识点,主要围绕下面几点: 既然数组是一个类, 那么编译后类名是什么?类路径呢? 为什么说动态加载不适合数组? 那应该如何动态加载一个数组? 部分内容参考 &l ...

  3. Java冷知识之——魔数(Magic Number)

    一. 编程中的魔数 在阿里巴巴Java开发手册编程规约第二节常量定义的第一条中写道: 魔法值(即魔数)指的是未经预先定义的常量,而反例中的 "Id#taobao_" 则叫做魔字符串 ...

  4. Java冷知识(三)编译器的花招之synthetic修饰符

    首先我们应该达成一个共识:很多的Java语法糖都是编译器赋予的,而JVM是一个与Java语言解耦的平台.有了这个共识,再来看今天的主角:synthetic修饰符. 我们在阅读JDK反射包源码时,会遇到 ...

  5. java冷知识:代码行数多少才算大项目?

    注:以下摘自:<软件工程概论> 郑人杰.殷人民编 java项目的大小衡量标准(以代码行作为计量标准的): 微型:只是一个人,甚至是半日工作在几天内完成的软件: 小型:一个人半年内完成的 2 ...

  6. JAVA 冷知识( break outer ;)

    如何在内循环结束外循环 关键字break和continue都是结束循环的作用,但是它只能结束它外面的第一层循环,如果代码里面是一层一层又一层的循环,你想直接结束最外层循环又该怎么做呢? 此时,就需要o ...

  7. 不看会后悔的100个超有趣冷知识(一)

    ------------------------------------多图预警------------------------------------------ 记事本冷知识 1.如何让记事本卡住 ...

  8. Java 15 年老兵都不一定了解的冷知识!

    你知道的越多,不知道的就越多,业余的像一棵小草! 成功路上并不拥挤,因为坚持的人不多. 编辑:业余草 推荐:https://www.xttblog.com/?p=5251 Java 15 年老兵都不一 ...

  9. 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事

    阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...

  10. 我的世界java版游戏崩溃_我的世界:MC不一样的冷知识,游戏崩溃?没想到你是这样的F3!...

    我的世界:MC不一样的冷知识,游戏崩溃?没想到你是这样的F3!我的世界:超实用的5大快捷操作,这个键按住10秒会发生神奇的事! 快捷操作一:拿起和放置物品 <我的世界>早期没有合成书的时候 ...

最新文章

  1. linux加密解密基础、PKI及SSL、创建私有CA
  2. php刷新公测,2021新时代来临,Z-BlogPHP 1.7 重磅公测
  3. 【本站原创】台大林轩田《机器学习基石》系列课程教材的习题解答和实现
  4. Linux篇 | 多网卡绑定技术 binding 和 网络组nmcli
  5. 我对CTO的理解 CTO要有技术魅力[转载]
  6. 3.Booleans and Conditionals
  7. 别以为JDK8有了红黑树,HashMap就不会有死循环问题!
  8. ajax上传文件报404_FTP上传文件需要注意哪些事项
  9. 解决 No module named ‘tensorflow.examples.tutorials‘
  10. 视频号的商业想象空间,在前天更新后,会在以下3个方面
  11. 用c++做算法题的注意事项(一)
  12. Step7 V5.6 win10版本安装包下载
  13. 如何在VScode中配置python第三方库
  14. 物联网技术应用成商业市场盈利关键
  15. 服务器系统2008r2企业版补丁,windows2008R2服务器更新补丁
  16. .输入一行字符串,含有数字和非数字字符以及空格等,如: df23adfd56 2343?23dgjop535 如果将其中所有连续出现的数字视为一个整数,要求统计在该字符串中共有多少个整数,并将这些数依
  17. 用户之间imp的问题
  18. 数据仓库建设之主题划分
  19. 2021.05.05青蛙过河
  20. 学海无涯!分享一些行业经验,最新整理

热门文章

  1. IOS UIKit基础控件的使用
  2. 开发者应知的 如何“推销自己”?
  3. Smith预估控制实验 simulink模型
  4. SCCM 2012 R2部署,SCCM配置(五)
  5. MySQL倒计时查询剩余时间xx年xx月xx天
  6. python 计算程序剩余时间的小程序
  7. vue综合项目——悦听音乐播放器
  8. 地铁与日本移动互联网
  9. 孤军奋战的百合网 下一城会在哪?
  10. 橘子学设计模式之原型模式