java基礎——泛型

Java泛型(generics)是JDK 5中引入的一個新特性,允許在定義類/接口/方法的時候使用類型參數(shù)(type parameter)。聲明的類型參數(shù)在使用時用具體的類型來替換。

1. 為什么使用泛型

泛型的好處在于編寫重用性更好的代碼,用來指定容器要持有什么類型的對象,由編譯器保證類型的正確性,不需要強制類型轉(zhuǎn)換。

2. 基本使用

2.1 泛型術(shù)語

  • List<E>被稱作泛型類型
  • List<E>中的E被稱為類型變量或類型參數(shù)
  • List<String>被稱為參數(shù)化類型。
  • List<String>中的String被稱為實際類型參數(shù)。
  • List被稱為原始類型。

2.2 泛型類

創(chuàng)建類時,指明想持有的對象,將其置于尖括號之內(nèi)。常見使用情景元組類庫:是一個單一對象,將一組對象直接打包存儲于其中。這個容器對象允許讀取其中的元素,但是不允許向其中存放新的對象。

public class TwoTuple<A, B> {
    public final A first;
    public final B second;
    public TwoTuple(A a, B b) {  
        first = a;
        second = b;
    }
}

泛型接口定義類似于泛型類在這里不具體描述。

2.3 泛型方法

泛型方法使得該方法能夠獨立于類而產(chǎn)生變化,建議在使用泛型方法可以取代整個類泛型話的時候選擇泛型方法。定義泛型方法,只需要泛型參數(shù)列表置于返回值之前。

public class Generators{
    public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gne, int n){
        for(int i = 0; i < n;i++){
            coll.add(gen.next());
        }
        return coll;
    }
}

泛型方法使用注意點:

  1. 定義方法所用的泛型參數(shù)需要在修飾符之后添加,如 public static <T>,如果有多個泛型參數(shù),可如此定義<K,V>。
  2. 類型參數(shù)推斷,區(qū)別于泛型類在使用時候必須指定類型參數(shù)值,泛型方法則不必,編譯器可以為我們找出具體類型,可以像調(diào)用普通方法一樣調(diào)用fill()方法。

3. 泛型擦除

java的泛型是在編譯器層面實現(xiàn)的,生成的字節(jié)碼中是不包含泛型中的類型信息的

在JAVA的虛擬機中并不存在泛型,泛型只是為了完善java體系,增加程序員編程的便捷性以及安全性而創(chuàng)建的一種機制,在JAVA虛擬機中對應泛型的都是確定的類型。
在編寫泛型代碼后,java虛擬中會把這些泛型參數(shù)類型都擦除,用相應的確定類型來代替,代替的這一動作叫做類型擦除,而用于替代的類型稱為原始類型,在類型擦除過程中,一般使用第一個限定的類型來替換,若無限定則使用Object。

有界類擦除前:

public class Eraser {    
    public static <A extends Comparable<A>> A max(Collection<A> xs) {        
        Iterator<A> xi = xs.iterator();        
        A w = xi.next();        
        while (xi.hasNext()) {            
            A x = xi.next();            
            if (w.compareTo(x) < 0)                
                w = x;        
            }        
        return w;    
    }
}

有界類擦除后:

public class Eraser {    
    public static Comparable max(Collection xs) {        
        Iterator xi = xs.iterator();        
        Comparable w = (Comparable)xi.next();        
        while (xi.hasNext()) {            
            Comparable x = (Comparable)xi.next();            
            if (w.compareTo(x) < 0)                
                w = x;        
            }        
        return w;    
    }
}

類型參數(shù)的邊界<A extends Comparable<A>>A,A必須為Comparable<A>的子類,按照類型擦除的過程,先將所有的類型參數(shù)轉(zhuǎn)換為最左邊界Comparable<A>,然后去掉參數(shù)類型A,得到最終的擦除后結(jié)果。

多邊界擦除
規(guī)則:使用其最左限制的擦除,即用第一個邊界的類型變量來替換。
如果類Pair聲明如下:
public class Pair<T extends Comparable & Serializable> { }
那么原始類型就是Comparable

4. 邊界與通配符

邊界使得你可以用于泛型的參數(shù)類型上設置限制條件,潛在效果你可以按照自己邊界類型調(diào)用方法。

4.1 邊界定義

<T extends BoundingType>這表示T應該是BoundingType的子類型,T和BoundingType可以是類,也可以是接口。另外,此處的extends表示的是子類型,不等同于繼承。多個邊界格式為:<T extends A & B & C>

示例代碼中T可以在編譯器調(diào)用HasColor擁有的方法。

class Colored<T extends HasColor> {    
    T item;    
    Colored(T item) {        
        this.item = item;    
    }    
    T getItem() {        
        return item;    
    }    
    //定義邊界之后,如下的方法調(diào)用是合法的    
    java.awt.Color color() {        
        return item.getColor();    
    }
}
4.2 邊界限定通配符
  • <? extends T> 表示類型的上界,表示參數(shù)化類型的可能是T或是T的子類。
  • <? super T> 表示類型下界,表示參數(shù)化類型是此類型的超類型(父類型),直至Object
  • <?> 表示類型無界,常見使用多個泛型參數(shù)允許其中某些為任何類型,例如:Map<String,?>

使用原則參考《Effective Java》:

  1. 如果要從集合中讀取類型T的數(shù)據(jù),并且不能寫入,可以使用 ? extends 通配符;(Producer Extends)
  2. 如果要從集合中寫入類型T的數(shù)據(jù),并且不需要讀取,可以使用 ? super 通配符;(Consumer Super)
  3. 如果既要存又要取,那么就不要使用無界通配符。
// demo1使用上界寫入會編譯錯誤
List<? extends Fruit> frutis = new ArrayList<Apple>();
fruits.add(new Apple());// complie error
fruits.add(new Fruit());// complie error
// demo2使用下界寫入正常
List<? super Apple> frutis = new ArrayList<Apple>();
fruits.add(new Apple());

對于demo1,因為List<? extends Fruit> 表示 “具有任何從Fruit繼承類型的列表”,編譯器無法確定List所持有的類型,所以無法安全的向其中添加對象。
對于demo2,list持有的是以Apple為下界,所以編譯器可以明確知道添加Apple或Apple的子類是安全的。

5. 注意問題

  • 不能用基本類型實例化類型參數(shù)
  • 不能實例化類型變量,例如new T(),T.class等
  • List.class,String[].class,int.class都是允許的,但就List<String>.class和List<?>.class則不合法
  • <Fruit> 和 <Apple>之間沒有繼承關系,所以List<Fruit>= new ArrayList<Apple>;會報編譯錯誤的,上界和下界為了解決該問題引入。
  • 類型安全和異構(gòu)容器: 一個集合如果可以預測返回值的類型,則是類型安全的;如果這個集合的鍵并不是同一個類型的,則稱之為異構(gòu)的。通過包裝一個Map,將其中的key保存為Class的,value保存為Object的,可以實現(xiàn)一個異構(gòu)容器。

參考文檔

http://blog.csdn.net/explorers/article/details/454837
http://hongjiang.info/java-generics/
http://www.importnew.com/19740.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • Java基礎-泛型的約束和局限性 Java中的泛型是一個非常重要知識點,在這里,簡單的介紹一下Java泛型的幾個注...
    瓊珶和予閱讀 1,734評論 1 0
  • 一、泛型 什么是泛型 Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型...
    阿敏其人閱讀 8,787評論 0 6
  • 1.什么是泛型 概括地講,泛型就是我們在定義類、接口或方法的時候?qū)⒛承┮玫降念惢蚪涌趨?shù)化,這樣定義的類、接口、...
    graceLiZi閱讀 630評論 0 0
  • 2.簡單泛型 -********Java泛型的核心概念:告訴編譯器想使用什么類型, 然后編譯器幫你處理一切細節(jié) 2...
    CodingHou閱讀 398評論 0 0
  • 本文大量參考Thinking in java(解析,填充)。 定義:多態(tài)算是一種泛化機制,解決了一部分可以應用于多...
    谷歌清潔工閱讀 473評論 0 2