13.协变与逆变原理深度剖析及实例演示

2018-01-21  本文已影响0人  leofight

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 禁止这样的事情以保证运行时的安全。但这样会有一些影响。

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

上一篇 下一篇

猜你喜欢

热点阅读