课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > 警惕泛型是不能协变和逆变的
  • 警惕泛型是不能协变和逆变的

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

  • 什么叫协变和逆变?昆明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--零基础全日制班

开班日期:12/29

Java--零基础业余班

Java--零基础业余班

开班日期:12/29

Java--周末提升班

Java--周末提升班

开班日期:12/29

Java--零基础周末班

Java--零基础周末班

开班日期:12/29

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56