?通配符类型
- <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类;(上界通配符)
- <? super T> 表示类型下界,表示参数化类型是此类型的超类型(父类型),直至Object;(下界通配符)
上界通配符<? extends T>不能往里存,只能往外取
public class test {
public static void main(String[] args) {
List<Class<? extends pet>> list = new LinkedList<Class<? extends pet>>();
list.add(new dog());
}
}
class pet {
}
class dog extends pet {
}
class cat extends dog {
}
list.add(new dog())
表示 “具有任何从dog继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。
List<? extends pet> list = new LinkedList<dog>();
list.add(new dog());
即使你指明了dog的类型,也不能用add方法添加一个dog对象
假设支持add()方法
List<? extends pet> list1 = new ArrayList<pet>();
List<? extends pet> list2 = new ArrayList<dog>();
List<? extends pet> list3 = new ArrayList<cat>();
如果List<? extends pet>支持add方法的话:
- list1可以add pet和所有pet的子类;
- list2可以add dog和所有dog的子类;
- list3可以add cat和所有cat的子类;
下面的代码我们是编译不能通过的:
list.add(new pet());//error
list.add(new dog());//error
原因:编译器只知道容器内是pet或者它的派生类,但具体是什么类型不知道。可能是pet?可能是dog?也可能是cat?编译器在看到后面用pet赋值以后,集合里并没有限定参数类型是“pet“.
所以通配符<?>和类型参数的区别就在于,对编译器来说所有的T都代表同一种类型。比如public <T> List<T> fill(T... t)
这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer。
所以这里的错误就在这里,List<? extends pet>里什么都放不进去。
List<? extends pet> list不能进行add,但是,这种形式还是很有用的,虽然不能使用add方法,但是可以在初始化的时候一个Season指定不同的类型。比如:
List<? extends pet> list1 = getPetList();//getPetList方法会返回一个pet的子类的list
另外,由于我们已经保证了List中保存的是Pet类或者他的某一个子类,所以,可以用get方法直接获得值:
List<? extends pet> list1 = new ArrayList<>();
Pet pet = list1.get(0);//读取出来的东西只能存放在pet或它的基类里。
Object object = list1.get(0);//读取出来的东西只能存放在pet或它的基类里。
Dog dog= list1.get(0);//读取出来的东西只能存放在pet或它的基类里。
下界<? super T>不影响往里存,但往外取只能放在Object对象里
PECS原则
- 频繁往外读取内容的,适合用上界Extends
- 经常往里插入的,适合用下界Super