1.Java中的协变及逆变
Java 中的泛型是不型变的,这意味着 List<String> 并不是 List<Object> 的子类型。
为什么这样? 如果 List 不是不型变的,它就没比 Java 的数组好到哪去,因为如下代码会通过编译然后导致运行时异常:
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!!即将来临的问题的原因就在这里。Java 禁止这样!
objs.add(1); // 这里我们把一个整数放入一个字符串列表
String s = strs.get(0); // !!! ClassCastException:无法将整数转换为字符串
因此,Java 禁止这样的事情以保证运行时的安全。但这样会有一些影响。
- 举例说明java中协变与逆变
class Animal{
}
class Cat extends Animal{
}
class Dog extends Animal{
}
协变
List<Cat> cats = new ArrayList<>();
List<? extends Animal> animals = cats;//可放Animal及Animal的子类
animals.add(new Cat());//无法编译通过,协变是生产者(Producer)只能获取,不能添加
逆变
List<Animal> animals = new ArrayList<>();
List<? super Animal> contravariantAnimals = animals;
contravariantAnimals.add(new Cat());
contravariantAnimals.add(new Dog());
Animal animal = contravariantAnimals.get(0);//无法编译通过,逆变是消费者(Consumer)只能添加,不能获取
- 小结
带 extends 限定(上界)的通配符类型使得类型是协变的(covariant)。
List<? super String> 是 List<Object> 的一个超类,称为逆变性(contravariance).
只能从中读取的对象为生产者,只能写入的对象为消费者。
PECS: Producer(生产者) Extends, Consumer(消费者) Super.
2.Kotlin中的协变及逆变
举例说明
协变
class ParameterizedProducer<out T>(private val value:T){
fun get():T{
return this.value
}
}
fun main(args: Array<String>) {
val parameterizedProducer = ParameterizedProducer("welcome");
val myRef:ParameterizedProducer<Any> = parameterizedProducer;
assertTrue(myRef is ParameterizedProducer<Any>)
}
逆变
class ParameterizedConsumer<in T>(private val value:T){
fun toString(value:T):String{
return value.toString()
}
}
fun main(args: Array<String>) {
val parameterizedConsumer = ParameterizedConsumer<Number>(1)
val myRef2:ParameterizedConsumer<Int> = parameterizedConsumer
assert(myRef2 is ParameterizedConsumer<Int>)
}
- 小结
在Kotlin中:Consumer in,Producer out
网友评论