泛型:
为了使类以适应只因参数不同,而对参数到操作基本相同而提供的一种占位符。
如:
public class test<T>{
private T t = null;
public void setT(T t){
this.t = t;
}
public T getT(){
return this.t;
}
}
在集合中为了提高代码的重用性,很多都使用泛型来指定存储的数据类型:
List<Integer> list = new ArrayList<Integer>();
但有时我们希望通过一个函数来操作这些集合类型:
如遍历,修改指定参数,添加参数:
public void print(List<Integer> list){
//遍历集合方法
}
//那么当我们想要遍历List<Number> list 时 ,此方法就不再使用
public void print(List<Number> list){
}
//由于泛型擦除,这两个方法会被认为相同,而无法编译
于是引入了通配符来解决以泛型类作为参数的方法:
//List<? extends T>
//List<? super T>
public void print(List<? extends T> list){
//此方法即可接收,泛型为T子类的集合类型
//相当于:
// List<? extends Number> list = new ArrayList<Integer>();
}
public void print(List<? super T> list){
//此方法即可接收,泛型为T父类的集合类型
//相当于:
// List<? super Integer> list = new ArrayList<Number>();
}
主要为了记录子类边界,父类边界对方法的影响:
T为<? extends T>
中的T
如果使用子类边界,那么则不能使用该集合中以泛型T为参数的方法:
public static void main(String[] args){
List<Integer> list = new ArrayList<Integer>();
test(list);
}
public void test(List<? extends Number> list){
//list.add(new Integer); 此类方法不能使用
/*
原因是:
凡是泛型为Number子类的参数均可传递进来,
这样就无法确定传递过来的参数的准确数据类型,
又如何对其进行操作呢
但是上帝关上了一扇门,一定会打开另一扇窗,
因为传递过来的参数的父类是明确的(Number类型),
通过父类引用指向子类对象,
就可以使用返回值为Number类型的方法
*/
Number number = list.get(0);
//此类返回值为泛型的参数就可以使用
}
如果使用父类边界,那么不能使用该集合中返回值为泛型T的方法
public void test2(List<? super Integer> list){
//list.get();
/*此类返回值为泛型的方法不能使用,
因为凡是Integer的父类泛型的集合均可传递,
那么久无法明确其父类,
而无法通过父类类型来返回参数
由于传递过来的是Integer的父类,父类类型可以存储操作自己的子类型
如:
List<Object> list = new ArrayList<Object>();
list.add(new Integer(1));//可行的
List<Number> list = new ArrayList<Number>();
list.add(new Integer(2));//也是可行的
*/
list.add(new Integer());
}
总结:
造成通配符子类边界<? extends T>
,与父类边界<? super T>
方法使用受限的原因,是因为他们都无法得知传递来的参数的具体类型;子类边界只能确定传递来的参数的子类型,父类边界只能确定传递来的参数的父类型;由于引用只能由父类指向子类,利用此特性,子类边界来完成返回值为原泛型的方法,父类边界来完成参数为原泛型的方法!
注:原泛型即为作为实参传递进方法的对象的泛型,如:
public static void main(String[] args){
//Integer即为原泛型
List<Integer> list = new ArrayList<Integer>();
this.test(list)
}
public void test(List<? extends Number>){
//操作
}