Java基础篇之泛型详解下篇

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 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List作为形式参数,那么如果尝试...
    时待吾阅读 1,073评论 0 3
  • 文章作者:Tyan博客:noahsnail.com 1. 什么是泛型 Java泛型(Generics)是JDK 5...
    SnailTyan阅读 788评论 0 3
  • 第8章 泛型 通常情况的类和函数,我们只需要使用具体的类型即可:要么是基本类型,要么是自定义的类。但是在集合类的场...
    光剑书架上的书阅读 2,160评论 6 10
  • 为什么需要泛型? 通过泛型可以定义类型安全的数据结构,而无须使用实际的数据类型(可扩展)。这能够显著提高性能并得到...
    一只好奇的茂阅读 1,274评论 2 39
  • 加西亚·马尔克斯在《爱在瘟疫蔓延时》里讲述了男主佛洛伦蒂诺喜欢上女主费尔明娜,靠信件维持的感情在一年之后的结局却跟...
    Ladymary于阅读 541评论 6 3