博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[改善Java代码]警惕泛型是不能协变和逆变的
阅读量:7039 次
发布时间:2019-06-28

本文共 2437 字,大约阅读时间需要 8 分钟。

什么叫做协变(covariance)和逆变(contravariance)?

在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是用一个窄类型替换宽类型,而逆变则是用宽类型覆盖窄类型.

协变:宽类型------>窄类型

逆变:窄类型------>宽类型

class Base{    public Number doStuff(){        return 0;    }}class Sub extends Base{    @Override    public Integer doStuff(){        return 0;    }}

子类的 doStuff 方法返回值的类型比父类方法要窄(Integer extend Number),此时 doStuff 方法就是一个协变方法,同时根据 Java 的覆写定义来看,这又属于覆写。那什么是逆变呢?代码如下:

class Base{    public void doStuff(Integer i){        }}class Sub extends Base{    public void doStuff(Number n){        }}

子类的 doStuff 方法的参数类型比父类要宽,此时就是一个逆变方法,子类扩大了父类方法的输入参数,但是根据覆写定义来看,doStuff 不属于覆写,只是重载而已。

由于此时的doStuff方法已经与父类没有任何关系了,只是子类独立扩展出的 一个行为,所以是否声明为doStuff方法名意义不大,逆变已经不具有特别的意义了.我们来重点关注一下协变.

public class Client {    public static void main(String[] args) {        Base base = new Sub();    }}

 

 base变量是否发生了协变?是的....发生了协变,base变量是Base类型,它是父类,而其赋值却是子类实例,也就是用窄类型覆盖了宽类型,这也叫做多态(Polymorphism),两者相同含义,在Java世界里,"重复发明"轮子的事情多了去了.

 

泛型是既不支持协变也不支持逆变.为什么?

(1)泛型不支持协变

数组和泛型很相似,一个是中括号,一个是尖括号,那我们就以数组为参照对象,看代码:

public class Client {    public static void main(String[] args) {        //数组支持协变        Number[] n = new Integer[10];        //编译不通过,泛型不支持协变        //List
ln = new ArrayList
();//报错 //Type mismatch: cannot convert from ArrayList
to List
}}

 

ArrayList是List的子类型,Integer是Number的子类型,里氏替换原则在此行不通了,原因就是Java为了保证运行期的安全性,必须保证泛型参数类型是固定的.

所以它不允许一个泛型参数可以同时包含两种类型,即使是父子类关系也不行.

泛型不支持协变,但可以使用通配符(Wildcard)模拟协变.代码如下:

//Number的子类型都可以是泛型参数类型        List
ln = new ArrayList
();

 

"? extends Number" 表示的意思是,允许Number所有的子类(包括自身)作为泛型参数类型,但在运行期只能是一个具体类型,或者是Integer或者是Double类型,

或者是Number类型,也就是说通配符只是在编码期有效,运行期必须是一个确定类型.

(2)泛型不支持逆变

Java虽然可以允许逆变存在,单在对类型赋值上是不允许逆变的,你不能把一个父类实例对象赋值给一个子类类型变量,泛型自然也不允许此种情况发生了,

但是它可以使用super关键字来模拟实现.代码如下:

//Integer的父类型(包括Integer)都可以是泛型参数类型        List
li = new ArrayList
();

 

"
"? super Integer" 的意思是可以把所有Integer父类型(自身,父类或接口)作为泛型参数,这里看着就像是把一个Number类型的ArrayList赋值给了Integer类型的List, 其外观类似于使用一个宽类型覆盖一个窄类型,它模拟了逆变的实现. 泛型既不支持协变也不支持逆变,带有泛型参数的子类型定义与我们经常使用的类类型也不相同,其基本的类型关系如下: 泛型通配符的Q&A 1.Integer是Number的子类型   √ 2.ArrayList
是List
的子类型 √ 3.Integer[] 是Number[] 的子类型 √ 4.List
是List
的子类型 × 5.List
是List
的子类型 × 6.
List
是List
的子类型 ×

 

Java的泛型不支持逆变和协变,只是能够实现逆变和协变.

 

转载地址:http://xxnal.baihongyu.com/

你可能感兴趣的文章
高性能的Python扩展:第一部分
查看>>
Qt Linguist介绍
查看>>
Qt Creator快捷键
查看>>
《C语言解惑》—— 2.2 printf输出整数或字符
查看>>
为什么在 Redis 实现 Lua 脚本事务?
查看>>
《草根自媒体达人运营实战》一一2.4 总结
查看>>
Linux 内核被指缺乏安全性
查看>>
《Unity 3D人工智能编程》——2.2 子弹类
查看>>
flatpickr 发布 V3 版本 ,Javascript 日期选择器
查看>>
Win10 Creators Update:自动激活磁盘清理工具
查看>>
《淘宝店铺 大数据营销+SEO+爆款打造 一册通》导读
查看>>
开源大数据周刊-第57期
查看>>
java中文乱码解决之道(六)—–javaWeb中的编码解码
查看>>
《数字图像处理与机器视觉——Visual C++与Matlab实现(第2版)》导读
查看>>
后台 JavaScript 编译改进 Chrome 性能
查看>>
数据结构课程设计实战
查看>>
Rabbit.js —— 国产 RESTful 应用开发框架
查看>>
GitLab 9.3.0-rc2 发布,代码托管平台
查看>>
《Adobe Illustrator CC经典教程》—第0课0.8节创建和编辑渐变
查看>>
《Dreamweaver CS6完美网页制作——基础、实例与技巧从入门到精通》——第2章 网页色彩知识2.1 网页配色基础...
查看>>