5.通配符
为了引出通配符的概念,先看如下代码:
List<Integer> ex_int= new ArrayList<Integer>();
List<Number> ex_num = ex_int; //非法的
上述第2行会出现编译错误,因为Integer
虽然是Number
的子类,但List<Integer>不是List<Number>
的子类型。
假定第2行代码没有问题,那么我们可以使用语句ex_num.add(newDouble())
在一个List中装入了各种不同类型的子类,这显然是不可以的,因为我们在取出List中的对象时,就分不清楚到底该转型为Integer还是Double了。
因此,我们需要一个在逻辑上可以用来同时表示为List<Integer>
和List<Number>
的父类的一个引用类型,类型通配符应运而生。在本例中表示为List<?>
即可。下面这个例子也说明了这一点,注释已经写的很清楚了。
public static void main(String[] args) {
FX<Number> ex_num = new FX<Number>(100);
FX<Integer> ex_int = new FX<Integer>(200);
getData(ex_num);
getData(ex_int);//编译错误
}
public static void getData(FX<Number> temp) { //此行若把Number换为“?”编译通过
//do something...
}
public static class FX<T> {
private T ob;
public FX(T ob) {
this.ob = ob;
}
}
6.上下边界
看了下面这个上边界的例子就明白了,下界FX<? supers Number>
的形式就不做过多赘述了。
public static void main(String[] args) {
FX<Number> ex_num = new FX<Number>(100);
FX<Integer> ex_int = new FX<Integer>(200);
getUpperNumberData(ex_num);
getUpperNumberData(ex_int);
}
public static void getUpperNumberData(FX<? extends Number> temp){
System.out.println("class type :" + temp.getClass());
}
public static class FX<T> {
private T ob;
public FX(T ob) {
this.ob = ob;
}
}
7.泛型的好处
**(1)类型安全 **
通过知道使用泛型定义的变量的类型限制,编译器可以更有效地提高Java程序的类型安全。
(2)消除强制类型转换
消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。所有的强制转换都是自动和隐式的。
(3)提高性能
Lits list1 = new ArrayList();
list1.add("CSDN_SEU_Cavin ");
String str1 = (String)list1.get(0);
List<String> list2 = new ArrayList<String>();
list2.add("CSDN_SEU_Cavin ");
String str2 = list2.get(0);
对于上面的两段程序,由于泛型所有工作都在编译器中完成,javac编译出来的字节码是一样的(只是更能确保类型安全),那么何谈性能提升呢?是因为在泛型的实现中,编译器将强制类型转换插入生成的字节码中,但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来了可能。
8.泛型使用的注意事项
(1)泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
(2)泛型的类型参数可以有多个。
(3)不能对确切的泛型类型使用instanceof
操作。如下面的操作是非法的,编译时会出错。
if(ex_num instanceof FX<Number>){
}
(4)不能创建一个确切的泛型类型的数组。下面使用Sun的一篇文档的一个例子来说明这个问题:
List<String>[] lsa = new List<String>[10]; // Not really allowed.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Unsound, but passes run time store check
String s = lsa[1].get(0); // Run-time error: ClassCastException.
这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给 oa[1]
赋上一个ArrayList<Integer>
而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException
,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。
下面采用通配符的方式是被允许的:
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Correct.
Integer i = (Integer) lsa[1].get(0); // OK
网友评论