泛型是一个很令人费解的概念,但却是理解类型系统的重要基石!
大部分内容摘自《kotlin极简教程》,加上了自己的一些理解,写的不好,请多指教!
型变有三种基本的形态:协变(covariant), 逆变(cotravariant),不变(invariant)
我们先从java了解的类型通配符介绍这三个概念:java中通配符有一下两种形式:
? extends T:子类型上界限定符,指定类型的上界(只能是T类型或者T的子类)
? super T :父类的下界限定符,指定类型的下界(只能是T类型或者是T类型的父类)
interface Animals{
void eat();
void run();
}
class Cat implements Animals{
@Override
public void eat() {}
@Override
public void run() { }
}
class Dog implements Animals{
@Override
public void eat() {}
@Override
public void run() {}
}
在这里我先用java写几个类,在java中我们知道:Animal是Cat和Dog的父类,可以直接使用Cat的子类和Dog的子类给Animals的一个对象赋值,那么ArrayList
我们试试看:
可以看到编译报错,所以是不可能这样赋值的!
那么泛型通配符就派上用处了。将ArrayList
也就是说ArrayList
这里的ArrayList<? extends Animals>并不能添加Animal及其子类对象的!因为如果能的话就既能添加Cat对象又能添加Dog对象,java是不允许的!但是能add(null)
上述这种情况就叫做协变(即F是C的父类,f(F)是f(C)的父类)所以 extends T>实现了泛型的协变!(f(F)类似于List
super T>实现了泛型的逆变!
逆变即(即F是C的父类,f(C)是f(F)的父类)
如果既不是逆变又不是协变,就是不变。
List super Number>,这里限制了List的内容只能是Number和Number父类。例如List
ArrayList
ArrayList super Number> numbers = new ArrayList
在这个list中我们不能添加Number的任一父类对象,但能添加Number和其所有子类的对象!
PECS(Producer-Extends, Consumer-Super)
那么我们什么时候使用extends,什么时候使用super呢!
《kotlin极简教程》书上用了一个stack API来举例说明:
public class Stack{
public Stack(){}
public void push(E v){}
public E pop(){}
public boolean isEmpty(){}
}
实现pushAll方法
public void pushAll(Iterable src){
for(E e : src)
push(e)
}
假设有一个实例化的stack
public void pushAll(Iterable<?extends E> src){
for(E e : src)
push(e)
}
而要实现popAll方法,将一个stack中的元素依次取出add至dst中,不用通配符时:
public void popAll(Stack dst) {
while(!isEmpty())
dst.push(pop())
}
如果要将一个stack
public void popAll(Stack<? super E> dst) {
while(!isEmpty())
dst.push(pop())
}
将其PECS称为Get and Put principle
在java.util.Collection的copy方法中完美地诠释了PECS:
public static void copy(List super E> dest, List extends E> src) {
int srcSize = src.size();
if(srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
...略
}
大致可以这么解释,在这个方法中,我们从src中取数据,即src是producer,然后将数据添加至dest,所以dest是consumer所以,PECS,前者就是super,后者就是extends。
kotlin:
java中有通配符,kotlin中则抛弃了这些,引用了生产者消费者概念,从上面的copy方法中也可以看出端倪。
kotlin把只负责安全读取数据的对象称为生产者,只负责安全写入数据的对象为消费者。kotlin中,生产者用