昆明java培训
达内昆明广州春城路

18487146383

热门课程

警惕泛型是不能协变和逆变的

  • 时间:2016-09-30
  • 发布:昆明Java培训
  • 来源:达内新闻

什么叫协变和逆变?昆明Java培训机构的老师今天就给大家讲。

在编程语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数、泛型、返回值)替换或交换的特性,简单的说,协变是一个窄类型替换宽类型,而逆变则是用宽类型覆盖窄类型。其实,在Java中协变和逆变我们已经用了很久了,只是我们没发觉而已,看如下代码:

class Base {

public Number doStuff() {

return 0;

}

}

class Sub extends Base {

@Override

public Integer doStuff() {

return 0;

}

}

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

class Base {

public void doStuff(Integer i) {

}

}

class Sub extends Base {

@Override

public void doStuff(Number n) {

}

}

子类的doStuff方法的参数类型比父类要宽,此时就是一个逆变方法,子类扩大了父类方法的输入参数,但根据覆写的定义来看,doStuff不属于覆写,只是重载而已。由于此时的doStuff方法已经与父类没有任何关系了,只是子类独立扩展出的一个行为,所以是否声明为doStuff方法名意义不大,逆变已经不具有特别的意义了,我们重点关注一下协变,先看如下代码是否是协变:

public static void main(String[] args) {

Base base = new Sub();

}

base变量是否发生了协变?是的,发生了协变,base变量是Base类型,它是父类,而其赋值却是在子类实例,也就是用窄类型覆盖了宽类型。这也叫多态,两者同含义。

说了这么多,下面再再来想想泛型是否支持协变和逆变呢,答案是:泛型既不支持协变,也不支持逆变。为什么会不支持呢?

(1)、泛型不支持协变:数组和泛型很相似,一个是中括号,一个是尖括号,那我们就以数组为参照对象,看如下代码:

public static void main(String[] args) {

//数组支持协变

Number [] n = new Integer[10];

//编译不通过,泛型不支持协变

List<Number> list = new ArrayList<Integer>();

}

ArrayList是List的子类型,Integer是Number的子类型,里氏替换原则在此行不通了,原因就是Java为了保证运行期的安全性,必须保证泛型参数的类型是固定的,所以它不允许一个泛型参数可以同时包含两种类型,即使是父子类关系也不行。

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

//Number子类型(包括Number类型)都可以是泛型参数类型

List<? extends Number> list = new ArrayList<Integer>();

" ? extends Number "表示的意思是,允许Number的所有子类(包括自身)作为泛型参数类型,但在运行期只能是一个具体类型,或者是Integer类型,或者是Double类型,或者是Number类型,也就是说通配符只在编码期有效,运行期则必须是一个确定的类型。

(2)、泛型不支持逆变

java虽然允许逆变存在,但在对类型赋值上是不允许逆变的,你不能把一个父类实例对象赋给一个子类类型变量,泛型自然也不允许此种情况发生了。但是它可以使用super关键字来模拟实现,代码如下:

//Integer的父类型(包括Integer)都可以是泛型参数类型

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

" ? super Integer "的意思是可以把所有的Integer父类型(自身、父类或接口)作为泛型参数,这里看着就像是把一个Number类型的ArrayList赋值给了Integer类型的List,其外观类似于使用一个宽类型覆盖一个窄类型,它模拟了逆变的实现。

泛型既不支持协变,也不支持逆变,带有泛型参数的子类型定义与我们经常使用的类类型也不相同,其基本类型关系如下表所示:

泛型通配符QA

问    答

Integer是Number的子类型?    正确

ArrayList<Integer>是List<Integer>的子类型?       正确

Integer[]是Number[]的子类型?         正确

List<Integer>是List<Number>的子类型?     错误

List<Integer>是List<?extends Integer>的子类型?        错误

List<Integer>是List<?super Integer>的子类型?   错误

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

了解详情请登陆昆明达内Java培训官网(km.Java.tedu.cn)!

上一篇:不同的场景使用不同的泛型通配符
下一篇:不要在常量和变量中出现易混淆的字母

昆明java培训机构:java未来的发展走向

昆明java培训机构:java学完可以干什么!

昆明java培训机构:如何搞定BAT和华为offer?有这份攻略就够了!

昆明java培训机构:学Java别担心枯燥和无聊

选择城市和中心
贵州省

广西省

海南省

扫一扫

了解更多干货