天天看點

java泛型介紹

一、泛型初衷

Java集合不會知道我們需要用它來儲存什麼類型的對象,是以他們把集合設計成能儲存任何類型的對象,隻要就具有很好的通用性。但這樣做也帶來兩個問題:

  –集合對元素類型沒有任何限制,這樣可能引發一些問題:例如想建立一個隻能儲存Dog對象的集合,但程式也可以輕易地将Cat對象“丢”進去,是以可能引發異常。

  –由于把對象“丢進”集合時,集合丢失了對象的狀态資訊,集合隻知道它盛裝的是Object,是以取出集合元素後通常還需要進行強制類型轉換。這種強制類型轉換既會增加程式設計的複雜度、也可能引發ClassCastException。

二、在集合中使用泛型

在集合中使用泛型後帶來如下優勢

   –程式再也不能“不小心”把其他對象“丢進”strList集合中;

  –程式更加簡潔,集合自動記住所有集合元素的資料類型,進而無需對集合元素進行強制類型轉換。

下面的代碼中,"不小心"把一個Integer對象"丢進"了集合。

三、什麼是泛型

  所謂泛型:就是允許在定義類、接口指定類型形參,這個類型形參在将在聲明變量、建立對象時确定(即傳入實際的類型參數,也可稱為類型實參)。

  JDK1.5改寫了集合架構中的全部接口和類,為這些接口、類增加了泛型支援,進而可以在聲明集合變量、建立集合對象時傳入類型實參。

四、泛型的“菱形”文法 <>

  如下代碼:

  在java 7 以前,<>中的粗體字代碼都是必須的,但是現在可以不帶粗體字代碼。Java自動推斷出ArrayList的<>裡應該是String還是String,Integer。

  現在改為:

第一段和第二段代碼是完全等價的。

泛型的簡單應用:

  

五、深入泛型

1.定義泛型接口、類

  定義Apple類時使用了泛型聲明

2.從泛型派生子類

A1繼承泛型類:

繼承Apple類,T被String代替。子類會繼承到String getInfo()和void setInfo()兩個方法。

正确寫法如下:

3.并不存在泛型類

  雖然可以把ArrayList<String>類當成ArrayList的子類,事實上ArrayList<String>類也确實是一種特殊的ArrayList類,這個ArrayList<String>對象隻能添加String對象作為集合元素。但實際上,系統并沒有為ArrayList<String>生成新的class檔案,而且也不會把ArrayList<String>當成新類來處理。

  實際上,泛型對其所有可能的類型參數,都具有同樣的行為,進而可以把相同的類被當成許多不同的類來處理。與此完全一緻的是,類的靜态變量和方法也在所有的執行個體間共享,是以在靜态方法、靜态初始化、或者靜态變量的聲明和初始化中不允許使用類型形參。

  系統中并不會真正生成泛型類,是以instanceof運算符後不能使用泛型類。

 六、類型通配符

這段代碼看上去沒有任何問題,方法的聲明也沒有任何問題。但是問題在于:調用該方法傳入的實際參數的值。例如:

編譯上面的程式,發生錯誤。

這說明List<String>對象不能被當成List<Object>對象使用,也就是說:List<String>類并不是List<Object>類的子類。

此外,數組和泛型有所不同:假設Foo是Bar的一個子類型(子類或者子接口),那麼Foo[]依然是Bar[]的自類型;但G<Foo>不是G<Bar>的子類型。

 七、?的用法

  為了表示各種泛型List的父類,我們需要使用類型通配符,類型通配符是一個問号(?),将一個問号作為類型實參傳給List集合,寫作:List<?>(意思是未知類型元素的List)。這個問号(?)被稱為通配符,它的元素類型可以比對任何類型。 

  在“六”中的程式,将

改為:

再次編譯就沒有了錯誤。

這裡的?可謂什麼都可以表示,是不是給它的權力太大了!!   當然我們有自己的解決辦法:設定類型通配符的上限 

  使用List<?>這種形式是,即表明這個List集合可以是任何泛型List的父類。但還有一種特殊的情形,我們不想這個List<?>是任何泛型List的父類,隻想表示它是某一類泛型List的父類。

  我們需要一種泛型表示方法,它可以表示所有Shape泛型List的父類,為了滿足這種需求,Java泛型提供了被限制的泛型通配符。被限制的泛型通配符的如下表示:List<?

extends Shape> 

上面定義了三個形狀類,Sharp抽象父類,Circle類和Rectangle類繼承了抽象類Sharp。

下面定義一個Canvas類,該畫布類不同的形狀。

修改如下:

這段代碼就沒有了錯誤。

  Java泛型不僅允許在使用通配符形參時設定類型上限,也可以在定義類型形參時設定上限,用于表示創給該類型形參的實際類型必須是該上限類型,或是該上限類型的子類。 例如:

 八、泛型方法

  如果定義類、接口是沒有使用類型形參,但定義方法時想自己定義類型形參,這也是可以的,JDK1.5還提供了泛型方法的支援。

  泛型方法的文法格式為:

    修飾符 <T , S> 傳回值類型 方法名(形參清單)

    {

      //方法體...

    }

  泛型方法的方法簽名比普通方法的方法簽名多了類型形參聲明,類型形參聲明以尖括号括起來,多個類型形參之間以逗号(,)隔開,所有類型形參聲明放在方法修飾符和方法傳回值類型之間。 

  與類、接口中使用泛型參數不同的是,方法中的泛型參數無需顯式傳入實際類型參數,因為編譯器根據實參推斷類型形參的值。它通常推斷出最直接的類型參數。 

 九、泛型方法與類型通配符的差別

  大時候都可以使用泛型方法來代替類型通配符。

  泛型方法允許類型形參被用來表示方法的一個或多個參數之間的類型依賴關系,或者方法傳回值與參數之間的類型依賴關系。如果沒有這樣的類型依賴關系,不應該使用泛型方法。

十、設定通配符的下限

  Java集合架構中的TreeSet<E>有一個構造器也用到了這種設定通配符下限的文法,如下所示:

    TreeSet(Comparator<? super E> c)

十一、擦除與轉換

  在嚴格的泛型代碼裡,帶泛型聲明的類總應該帶着類型參數。但為了與老的Java代碼保持一緻,也允許在使用帶泛型聲明的類時不指定類型參數。如果沒有為這個泛型類指定類型參數,則該類型參數被稱作一個raw type(原始類型),預設是該聲明該參數時指定的第一個上限類型。

  當把一個具有泛型資訊的對象賦給另一個沒有泛型資訊的變量時,則所有在尖括号之間的類型資訊都被扔掉了。比如說一個List<String>類型被轉換為List,則該List對集合元素的類型檢查變成了成類型變量的上限(即Object),這種情況被為擦除。

 ……待續

下一篇: AC自動機

繼續閱讀