Java泛型讲解
实例讲解Java泛型里的协变和逆变。
Java泛型里的协变和逆变
通过实例来看问题,
1
|
// 定义三个类: Benz -> Car -> Vehicle,它们之间是顺次继承关系
|
我们只需关注Utils<Car>.copy()函数即可,两个参数from, to均为list,
- 对
from的要求:其中的对象必须是Car或者Car的子类,即可以用Car来引用这些对象 - 对
to的要求:它必须可以保存Car类型的对象,即其元素的类型必须是Car或者Car的父类
接下来看看该函数的使用情况,carUtils.copy(vehicles, benzs);,参数的含义是:
List<? extents Car>:这个类型集合(List<Car>,List<Benz>)里的元素可以使用替换原则List<? super Car>:这个类型集合(List<Car>,List<Vehicle>)里的元素也可以使用替换原则
都可以使用替换原则了,但是他们有何区别呢?
List<? extents Car>:List<? extents Car>与? extends Car的序关系是一致的List<? super Car>:List<? super Car>与? super Car的序关系是相反的
其中,? extends Car, ? super Car, List<? extents Car>, List<? super Car>
均为类型集合,序关系小的可以替换序关系大的。其实在类型系统里面,Liskov替换原则可以
进一步推广为: 任何序关系大的类型可以出现的地方,序关系小的类型一定可以出现。
而继承关系是一种特殊的序关系,当然这需要语言的类型系统支持才可以。
协变和逆变
定义(wikipedia)
covariantif it preserves the ordering of types (≤), which orders types from more specific to more generic;contravariantif it reverses this ordering;bivariantif both of these apply (i.e., bothI<A>≤I<B>andI<B>≤I<A>at the same time);invariantornonvariantif neither of these applies.
理解
设T是一个类型集合(type set),其中的元素是一个个类型,如Vehicle, Car, Benz,S<T>是一个根据T生成的类型集合(如List<T>),其中的元素也是一个个类型,如S<Vehicle>,S<Car>, S<Benz>,那么我们有如下定义,
- 如果集合
S<T>里的序关系跟集合T里的序关系一致,那么就说S<T>跟T是协变关系 - 如果集合
S<T>里的序关系跟集合T里的序关系相反,那么就说S<T>跟T是逆变关系
然后,根据序关系的大小就可以使用替换原则了。那函数Utils<Car>.copy()的参数
为啥要用? extends T,? super T而不直接使用T呢,void copy(List<T> to, List<T> from),
把T替换成Car之后,要使用这个函数就只能使用List<Car>了,但是很明显,我们完全可以
将一个List<Benz> copy 到一个List<Car>或者List<Vehicle>里面,要怎么解决呢?
当然是使用协变和逆变:
- 对于
from参数,? extends T表示跟T满足协变关系的List<T>就可以使用替换原则 - 对于
to参数,? super T表示跟T满足逆变关系的List<T>就可以使用替换原则
这样就不用仅仅局限到List<Car>了。
协变、逆变使用的时机
然后问题又来了,什么时候使用协变,什么时候使用逆变呢?
仔细观察(C#里面已经观察好久了)就会发现,
- 如果只是读取的话,那么满足协变关系的类型可以使用替换原则
- 如果只是写入的话,那么满足逆变关系的类型可以使用替换原则
比如上面的函数,void copy(List<? super Car> to, List<? extends Car> from);,
从from里面读取数据,则完全可以从List<Car>,List<Benz>里面读取,
而往to里面写入数据,则完全可以往List<Car>,List<Vehicle>里面写入,
所以from使用满足协变关系的类型而to使用满足逆变关系的类型。事实上,在C#,Kotlin里,
直接使用out, in来表示协变关系和逆变关系,比如Kotlin里面这样定义copy函数,fun copy(to: List<in Car>, from: List<out Car>),然后就可以这样使用了,
copy(cars, benzs)copy(cars, cars)copy(vehicles, benzs)copy(vehicles, cars)
转载来自 http://ybin.cc/programming/java-variance-in-generics/

浙公网安备 33010602011771号