Java泛型程式設計最全總結__其他

來源:互聯網
上載者:User

JAVA泛型程式設計筆記 1介紹

Java泛型程式設計是JDK1.5版本後引入的。泛型讓編程人員能夠使用類型抽象,通常用於集合裡面。下面是一個不用泛型例子:

 

 

如果Foo是Bar的子類型,G是一種帶泛型的類型,則G<Foo>不是G<Bar>的子類型。這也許是泛型學習裡面最讓人容易混淆的一點

? extends Object 重在指定接收可以是子類型  直接?表示接收任何類型(不可add)

 

? super String  重在add的類型是String的類或本身

 

注意泛型方法的格式(泛型作為方法的參數),型別參數<T>需要放在函數傳回值之前。然後在參數和傳回值中就可以使用泛型參數了

 

 

在<>中表示泛型的可以是任意字母(<T>),和問號(泛型類<?>)一樣的功能(通配),沒有具體泛型類(list,conllection)就用<T>,其中表示

其他的讀寫時的區別一樣

 

public abstract class PacketProcesser<P extends PaPacket,T extends Answer>  Java代碼   List myIntList=new LinkedList(); //1   myIntList.add(newInteger(0)); //2   Integer x=(Integer)myIntList.iterator().next(); //3    

注意第3行代碼,但這是讓人很不爽的一點,因為程式員肯定知道自己儲存在List裡面的物件類型是Integer,但是在返回列表中元素時,還是必須強制轉換類型,這是為什麼呢。原因在於,編譯器只能保證迭代器的next()方法返回的是Object類型的對象,為保證Integer變數的型別安全,所以必須強制轉換。

這種轉換不僅顯得混亂,更可能導致類型轉換異常ClassCastException,運行時異常往往讓人難以檢測到。保證列表中的元素為一個特定的資料類型,這樣就可以取消類型轉換,減少發生錯誤的機會, 這也是泛型設計的初衷。下面是一個使用了泛型的例子:

  Java代碼   List<Integer> myIntList=newLinkedList<Integer>(); //1’   myIntList.add(newInteger(0)); //2’   Integerx=myIntList.iterator().next(); //3’    

       在第1行代碼中指定List中儲存的物件類型為Integer,這樣在擷取列表中的對象時,不必強制轉換類型了。

  2定義簡單的泛型

下面是一個引用自java.util包中的介面List和Iterator的定義,其中用到了泛型技術。

  Java代碼   public interface List<E> {   <span style="white-space: pre;">    </span>void add(E x);   <span style="white-space: pre;">    </span>Iterator<E> iterator();   }   public interface Iterator<E> {   <span style="white-space: pre;">    </span>E next();   <span style="white-space: pre;">    </span>boolean hasNext();   }    

       這跟原生類型沒有什麼區別,只是在介面後面加入了一個角括弧,角括弧裡面是一個型別參數(定義時就是一個格式化的型別參數,在調用時會使用一個具體的類型來替換該類型)。

       也許可以這樣認為,List<Integer>表示List中的型別參數E會被替換成Integer。

  Java代碼   public interface IntegerList {   <span style="white-space: pre;">    </span>void add(Integer x)   <span style="white-space: pre;">    </span>Iterator<Integer> iterator();   }    

       類型擦除指的是通過型別參數合并,將泛型型別執行個體關聯到同一份位元組碼上。編譯器只為泛型型別產生一份位元組碼,並將其執行個體關聯到這份位元組碼上,因此泛型型別中的靜態變數是所有執行個體共用的。此外,需要注意的是,一個static方法,無法訪問泛型類的型別參數,因為類還沒有執行個體化,所以,若static方法需要使用泛型能力,必須使其成為泛型方法。類型擦除的關鍵在於從泛型型別中清除型別參數的相關資訊,並且再必要的時候添加類型檢查和類型轉換的方法在使用泛型時,任何具體的類型都被擦除,唯一知道的是你在使用一個對象。比如:List<String>List<Integer>在運行事實上是相同的類型。他們都被擦除成他們的原生類型,即List因為編譯的時候會有類型擦除,所以不能通過同一個泛型類的執行個體來區分方法,如下面的例子編譯時間會出錯,因為類型擦除後,兩個方法都是List類型的參數,因此並不能根據泛型類的類型來區分方法。

  Java代碼   /*會導致編譯時間錯誤*/     public class Erasure{               public void test(List<String> ls){                   System.out.println("Sting");               }               public void test(List<Integer> li){                   System.out.println("Integer");               }     }    

       那麼這就有個問題了,既然在編譯的時候會在方法和類中擦除實際類型的資訊,那麼在返回對象時又是如何知道其具體類型的呢。如List<String>編譯後會擦除掉String資訊,那麼在運行時通過迭代器返回List中的對象時,又是如何知道List中儲存的是String類型對象呢。

       擦除在方法體中移除了類型資訊,所以在運行時的問題就是邊界即對象進入和離開方法的地點,這正是編譯器在編譯期執行類型檢查並插入轉型代碼的地點。泛型中的所有動作都發生在邊界處:對傳遞進來的值進行額外的編譯期檢查,並插入對傳遞出去的值的轉型。

  3.泛型和子類型

為了徹底理解泛型,這裡看個例子:(Apple為Fruit的子類)

  Java代碼   List<Apple> apples = new ArrayList<Apple>(); //1   List<Fruit> fruits = apples; //2    

第1行代碼顯然是對的,但是第2行是否對呢。我們知道Fruit fruit = new Apple(),這樣肯定是對的,即蘋果肯定是水果,但是第2行在編譯的時候會出錯。這會讓人比較納悶的是一個蘋果是水果,為什麼一箱蘋果就不是一箱水果了呢。可以這樣考慮,我們假定第2行代碼沒有問題,那麼我們可以使用語句fruits.add(new Strawberry())(Strawberry為Fruit的子類)在fruits中加入草莓了,但是這樣的話,一個List中裝入了各種不同類型的子類水果,這顯然是不可以的,因為我們在取出List中的水果對象時,就分不清楚到底該轉型為蘋果還是草莓了。

通常來說,如果Foo是Bar的子類型,G是一種帶泛型的類型,則G<Foo>不是G<Bar>的子類型。這也許是泛型學習裡面最讓人容易混淆的一點。

  4.萬用字元 4.1萬用字元。

先看一個列印集合中所有元素的代碼。

  Java代碼   //不使用泛型   void printCollection(Collection c) {                   <span style="white-space: pre;">    </span>Iterator i=c.iterator();   <span style="white-space: pre;">    </span>for (k=0;k < c.size();k++) {   <span style="white-space: pre;">        </span>System.out.println(i.next());   <span style="white-space: pre;">    </span>}   }    

 

  Java代碼   //使用泛型   void printCollection(Collection<Object> c) {   for (Object e:c) {   System.out.println(e);   }   }    

     很容易發現,使用泛型的版本只能接受元素類型為Object類型的集合如ArrayList<Object>();如果是ArrayList<String>,則會編譯時間出錯。因為我們前面說過,Collection<Object>並不是所有集合的超類。而老版本可以列印任何類型的集合,那麼如何改造新版本以便它能接受所有類型的集合呢。這個問題可以通過使用萬用字元來解決。修改後的代碼如下所示:

  Java代碼   //使用萬用字元。,表示可以接收任何元素類型的集合作為參數   void printCollection(Collection<?> c) {   <span style="white-space: pre;">    </span>for (Object e:c) {   <span style="white-space: pre;">        </span>System.out.println(e);   <span style="white-space: pre;">    </span>}   }    

這裡使用了萬用字元。指定可以使用任何類型的集合作為參數。讀取的元素使用了Object類型來表示,這是安全的,因為所有的類都是Object的子類。這裡就又出現了另外一個問題,如下代碼所示,如果試圖往使用萬用字元。的集合中加入對象,就會在編譯時間出現錯誤。需要注意的是,這裡不管加入什麼類型的對象都會出錯。這是因為萬用字元。表示該集合儲存的元素類型未知,可以是任何類型。往集合中加入元素需要是一個未知元素類型的子類型,正因為該集合儲存的元素類型未知,所以我們沒法向該集合中添加任何元素。唯一的例外是null,因為null是所有類型的子類型,所以儘管元素類型不知道,但是null一定是它的子類型。

  Java代碼  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.