Java泛型中的?
指通配符,泛指所有类型。而<? extends T>
指的是上界通配符,? super T
指的是下界通配符。
1 为什么要用通配符和边界?
举个例子,我们假设有一个Cat
类表示所有的猫科动物,那么显然加菲猫类Garfield
是Cat
的一个子类。如果我们有一个猫科动物的笼子List<Cat>
,我们理所当然地认为这个笼子也可以关加菲猫。但是,下面这行代码却在编译期报错了。
List<Cat> cats = new ArrayList<Garfield>();
所以,我们可以知道,就算容器中存放的对象类型有继承关系,容器间却是没有继承关系的。为了让容器间也存在类似的继承关系,通配符边界就应运而生了。
2 什么是上界?
List<? extends T>
表示这个List
对象可以存放T
和T
的一切子类对象,由此,编译器认为List<? extends T>
是List<T>
的父类,所以我们按照如下方式赋值是不会报错的。
List<? extends Cat> cats = new ArrayList<Garfield>();
然而,使用<?extends Cat>
定义的List
是不可以再存放任何元素的,因为编译器仅仅知道你集合中对象的类型都是?
,具体是啥类型并不知道,所以肯定不能让你乱放元素,万一你放了个Object
对象,那集合中的对象类型就乱套了,不再是Cat
或Cat
的子类。
使用<?extends Cat>
定义的List
的get()
方法正常,因为取出来的元素都可以向上转型成Cat
对象。
3 什么是下界?
List<? super T>
表示这个List
对象可以存放T
和T
的一切父类对象。但是,List<? super Cat>
是List<Cat>
的父类,但是却不是List<Garfield>
的父类。也就是说,下面这行代码编译期就会报错。
List<? super Cat> cats = new ArrayList<Garfield>();
但是这样赋值就不会报错:
List<? super Cat> cats = new ArrayList<Object>();
使用<? super Cat>
定义的List
的get()
方法会部分失效,因为所有取出来的对象都会转型为Object
对象,从而丢失对象原来的信息。set()
方法正常。
import java.util.ArrayList;
import java.util.List;
public class AnimalCatGarfield {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
List<Garfield> garfields = new ArrayList<>();
animals.add(new Animal());
cats.add(new Cat());
garfields.add(new Garfield());
// 编译出错,extendsCatFromAnimal只能赋值Cat或Cat子类的集合
List<? extends Cat> extendsCatFromAnimal = animals;
// 编译成功
List<? super Cat> superCatFromAnimal = animals;
// 下面两行均编译成功
List<? extends Cat> extendsCatFromCat = cats;
List<? super Cat> superCatFromCat = cats;
// 编译成功
List<? extends Cat> extendsCatFromGarfield = garfields;
// 编译出错,superCatFromGarfield只能赋值Cat或Cat的父类集合
List<? super Cat> superCatFromGarfield = garfields;
// 测试add方法
// 下面三行均编译失败,除了null以外,任何元素都不能被添加进<? extends T>集合内
extendsCatFromCat.add(new Animal());
extendsCatFromCat.add(new Cat());
extendsCatFromCat.add(new Garfield());
// 编译失败,只能添加Cat或Cat子类的集合
superCatFromCat.add(new Animal());
// 下面两行均编译成功
superCatFromCat.add(new Cat());
superCatFromCat.add(new Garfield());
// 测试get方法
// 所有super操作都能够返回元素,但是泛型丢失,只能返回Object对象
// 以下extends操作能够返回元素
Object catExtends2 = extendsCatFromCat.get(0);
Cat catExtends1c = extendsCatFromCat.get(0);
// 编译出错,虽然Cat集合从Garfield赋值而来,但是类型擦除后,是不知道的
Garfield garfield1 = extendsCatFromGarfield.get(0);
}
}