Java的泛型只有通配符?和extends、super,没有语法上的协变和逆变。
什么是协变和逆变?
在混合OO和多态性时,一个核心问题是:如果T’是T一个子类,Container[T’]应该被看做是Container[T]的子类吗?Java中是不允许的。List<String>和List<Object>无关。
协变:covariant,C[T’]是 C[T] 的子类,
在Java中,List<Integer>是List<? extends Number>的子类。
协变很容易理解。一个方法需要List<? extends Number>参数时,可以传递一个List<Integer>.
又如:
Holder<? extends Animal> birdHolder = new Holder<Bird>(new Bird()); // 编译OK
由于Java并没有协变,所以写成下面的样子是错误的,编译不通过。
Holder<Animal> birdHolder = new Holder<Bird>(new Bird()); //编译不通过
逆变:contravariant,C[T] 是 C[T’]的子类
在Java中,List<Number>是List<? super Integer>的子类。
又如:
Holder<? super Bird> animalHolder = new Holder<Animal>(new Animal());
由于Java没有逆变,所以写成下面是错误的。
Holder<Bird> animalHolder = new Holder<Animal>(new Animal());// 编译不通过
逆变似乎很难理解。那么逆变用在什么地方呢?
在Java 8以前,? super主要用在一些类的比较方法上;
Java 8中,实际上Function的第一个参数经常采用逆变(? super T).
Function<Animal, String> a = (Animal animal) -> ThirdParty.makeSound(animal);
Function<Bird, String> func = a; // 编译错误;两个Function没有关系。
使用super逆变,使Function<Animal,String>成为Function<? super Bird, String>的子类型。
Function<Animal, String> a = (Animal animal) -> ThirdParty.makeSound(animal);
Function<? super Bird, String> func = a;
协变和逆变本质上体现的还是Liskov替换原则(Liskov substitution principle), 即“子类可以替换父类”
不变invariant:C[T] 和 C[T’]无关
不变是Java中默认的机制。List<Bird>和List<Animal>没有关系。
网友评论