在定义泛型时,我们可以通过extends来限定泛型类型的上限,也可以通过super来限定下限,这两个限定字一般会和?等关键字搭配使用。
比如有这样的代码List<? super Father> dest,这里,super包含“高于”的意思,? Super Father就表示dest存放的对象应当“以Father为子类”;换句话说,在dest里,可以存放任何子类是Father类的对象。
再来看个extends的用法。比如有这样的代码,List<? extends Father> src,extends用来表示继承,这里的src可以存放以”Father”为父类的对象;也就是说,src可以存放任何Father对象的子类。
在实际的项目里,我们一般从List<? extends Father> src这类的集合里读元素,而从List<? super Father> dest这样的集合里写元素。通过下面的GenericExtends.java例子,再来了解extends,super和?的用法。
1 import java.util.ArrayList;
2 import java.util.List;
3 //定义一个空的父类和空的子类
4 class Father{ }
5 class Son extends Father{}
6 //这是个包含main方法的主类
7 public class GenericExtends {
8 //这个方法里,将把src里的对象复制到dest里
9 static void copy(List<? super Father> dest,
10 List<? extends Father> src) {
11 for (int i=0; i<src.size(); i++)
12 { dest.add(src.get(i)); }
13 }
在第9行copy方法的两个参数里,我们看到了两个包含extends和super泛型的参数。在方法体的for循环里,我们的做法符合刚才讲到的原则:从带extends泛型的集合里读,往带super泛型的集合里写。
public static void main(String[] args) {
15 Father f = new Father();
16 Son s = new Son();
17 //创建了一个带Father泛型的集合,并向其中放了一个元素
18 List<Father> srcFatherList = new ArrayList<Father>();
19 srcFatherList.add(f);
20 List<Father> destFatherList = new ArrayList<Father>();
21 //通过copy方法,把元素复制进了destFatherList里
22 copy(destFatherList,srcFatherList);
23 //这里的输出是1,说明copy方法成功地往destFatherList里写了元素
24 System.out.println(destFatherList.size());
25 }
26 }
在定义方法的参数时,我们可以用带extends和super的泛型来确保输入参数类型的准确性。除此之外,这两种泛型的用处不大,比如在main函数的第22行里,调用copy方法时,我们传入的参数都是List<Father>类型。
下面我们来展示些错误的用法:
错误用法一:用带问号的类型实例化集合对象。
1 List<?> list = new ArrayList<String>(); //正确
2 //List<?> list = new ArrayList<?>(); //错误
第1行里,虽然在等号的左边我们用到了问号,但在右边,我们确立了泛型类型是String,这个是正确的。与之相比,在等号的左边和右边我们都用了问号,这是错误的,因为编译器不知道list集合该采用哪种泛型类型。
错误用法二:向包含<? extends Father>泛型的集合里写。
1 List<? extends Father> list = new ArrayList<Father>();
2 //list.add(f); //error
第2行会报语法错,原因是编译器不知道这个基于Father的子类型究竟是什么;因为没法确定,为了保证类型安全,所以就不允许往里面加数据。”
错误用法三:从包含<? super Father>泛型的集合里读。
- List<? super Father> list1 = new ArrayList<Father>();
- list.add(f); //正确
- //list.get(0);//错误
第3行会报语法错,原因是编译器不知道该用哪种Father的父类来接收get的返回值;于是,同样为了保证类型安全,所以就不允许读。
从上述的第二和第三种错误的用法里,我们能感受到,extends和super这两种定义泛型的用法除了在定义方法参数之外,还真没其他合适的用途。
关于集合类的面试文章汇总:
Java集合方面的面试题:对比ArrayList和Vector对象,分析Vector为什么不常用
Java集合方面的面试题:ArrayList和LinkedList有什么差别?分别适用于哪些场景?
Java集合方面的面试题:TreeSet、HashSet和LinkedHashSet的各自特点
这是我的公众号,其中包含了大量面试文章,同时我自己出了多本Python和Java方面的书籍,会定期在公众号里发书的电子版。请大家关注下我的公众号,谢谢了。