课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > Java 泛型里的协变和逆变
  • Java 泛型里的协变和逆变

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

  • 程序员的资料

    Java泛型里的协变和逆变

    java培训班通过实例来讨论问题:

    // 定义三个类: Benz -> Car -> Vehicle,它们之间是顺次继承关系class Vehicle {}class Car extends Vehicle {}class Benz extends Car {}// 定义一个util类,其中用到泛型里的协变和逆变class Utils { T get(List list, int i) { return list.get(i); } void put(List list, T item) { list.add(item); } void copy(List to, List from) { for(T item : from) { to.add(item); } }}// 测试函数void test() { List vehicles = new ArrayList<>(); List benzs = new ArrayList<>(); Utils carUtils = new Utils<>(); carUtils.put(vehicles, new Car()); Car car = carUtils.get(benzs, 0); carUtils.copy(vehicles, benzs);}

    我们只需关注Utils.copy()函数即可,两个参数from, to均为list,

    对from的要求:其中的对象必须是Car或者Car的子类,即可以用Car来引用这些对象

    对to的要求:它必须可以保存Car类型的对象,即其元素的类型必须是Car或者Car的父类

    接下来看看该函数的使用情况,carUtils.copy(vehicles, benzs);,参数的含义是:

    List:这个类型集合(List, List)里的元素可以使用替换原则

    List:这个类型集合(List,List)里的元素也可以使用替换原则

    都可以使用替换原则了,但是他们有何区别呢?

    List:List与? extends Car的序关系是一致的

    List:List与? super Car的序关系是相反的

    其中,? extends Car, ? super Car, List, List

    均为类型集合,序关系小的可以替换序关系大的。其实在类型系统里面,Liskov替换原则可以

    进一步推广为: 任何序关系大的类型可以出现的地方,序关系小的类型一定可以出现。

    而继承关系是一种特殊的序关系,当然这需要语言的类型系统支持才可以。

    协变和逆变

    定义(wikipedia)

    covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic;

    contravariant if it reverses this ordering;

    bivariant if both of these apply (i.e., both I ≤ I and I ≤ I at the same time);

    invariant or nonvariant if neither of these applies.

    理解

    设T是一个类型集合(type set),其中的元素是一个个类型,如Vehicle, Car, Benz,

    S是一个根据T生成的类型集合(如List),其中的元素也是一个个类型,如S,

    S, S,那么我们有如下定义,

    如果集合S里的序关系跟集合T里的序关系一致,那么就说S跟T是协变关系

    如果集合S里的序关系跟集合T里的序关系相反,那么就说S跟T是逆变关系

    然后,根据序关系的大小就可以使用替换原则了。那函数Utils.copy()的参数

    为啥要用? extends T,? super T而不直接使用T呢,void copy(List to, List from),

    把T替换成Car之后,要使用这个函数就只能使用List了,但是很明显,我们完全可以

    将一个List copy 到一个List或者List里面,要怎么解决呢?

    当然是使用协变和逆变:

    对于from参数,? extends T表示跟T满足协变关系的List就可以使用替换原则

    对于to参数,? super T表示跟T满足逆变关系的List就可以使用替换原则

    这样就不用仅仅局限到List了。

    协变、逆变使用的时机

    然后问题又来了,什么时候使用协变,什么时候使用逆变呢?

    仔细观察(C#里面已经观察好久了)就会发现,

    如果只是读取的话,那么满足协变关系的类型可以使用替换原则

    如果只是写入的话,那么满足逆变关系的类型可以使用替换原则

    比如上面的函数,void copy(List to, List from);,

    从from里面读取数据,则完全可以从List,List里面读取,

    而往to里面写入数据,则完全可以往List,List里面写入,

    所以from使用满足协变关系的类型而to使用满足逆变关系的类型。事实上,在C#,Kotlin里,

    直接使用out, in来表示协变关系和逆变关系,比如Kotlin里面这样定义copy函数,

    fun copy(to: List, from: List),然后就可以这样使用了,

    copy(cars, benzs)

    copy(cars, cars)

    copy(vehicles, benzs)

    copy(vehicles, cars)

    by:iGNU

    推荐文章

上一篇:比尔盖茨新预测:10年内机器人将比人类更聪明

下一篇:java培训班:Java 中的接口和抽象类

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址: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