黑馬程式設計師 java泛型
---------------------- ASP.Net+Unity開發、.Net培訓、期待與您交流! ----------------------
一、概念介紹:
Java泛型(generics)是JDK 5中引入的一個新特性,允許在定義類和介面的時候使用型別引數(type parameter)。宣告的型別引數在使用時用具體的型別來替換。泛型最主要的應用是在JDK 5中的新集合類框架中。
二、引入泛型的好處
1、將執行時期出現問題ClassCastException,轉移到了編譯時期。
方便於程式設計師解決問題。讓執行時問題減少,提高了程式安全性。
2、避免了強制轉換麻煩
下面是個不用泛型的例子:
List list = new LinkedList(); list.add(0); Integer x=(Integer)list.iterator().next();
注意第3行程式碼,這是讓人很不爽的一點,因為程式設計師肯定知道自己儲存在List裡面的物件型別是Integer,但是在返回列表中元素時,還是必須強制轉換型別,這是為什麼呢?原因在於,編譯器只能保證迭代器的next()方法返回的是Object型別的物件,為保證Integer變數的型別安全,所以必須強制轉換。
這種轉換不僅顯得混亂,更可能導致型別轉換異常ClassCastException,而執行時異常往往讓人難以檢測到。保證列表中的元素為一個特定的資料型別以取消型別轉換,減少發生錯誤的機會,
下面是用泛型的例子:
List<Integer> list = new LinkedList<Integer>(); list.add(0); Integer x = list.iterator().next();
在第1行程式碼中指定List中儲存的物件型別為Integer,這樣在獲取列表中的物件時,就不必強制轉換型別了。
三、定義泛型:
定義泛型跟定義原生型別沒有什麼區別,只是在類或介面後面加入了一個尖括號,尖括號裡面是一個型別引數(定義時就是一個格式化的型別引數,在呼叫時會使用一個具體的型別來替換該型別)
程式碼示例:
//原始碼中的List介面 和 Iterator 介面的例子 public interface List<E> { void add(E x); Iterator<E> iterator(); } public interface Iterator<E> { E next(); boolean hasNext(); }
四、泛型的特點:
泛型的最大特就是型別可擦除。
Java中的泛型基本上都是在編譯期來實現的。在生成的Java位元組程式碼中是不包含泛型中的型別資訊的。使用泛型的時候加上的型別引數,會被編 譯器在編譯的時候去掉。這個過程就稱為型別擦除。
程式碼示例:
public static void main(String[] args) { ArrayList<String> arr1 = new ArrayList<String>(); ArrayList<Integer> arr2 = new ArrayList<Integer>(); System.out.println(arr1.getClass()); System.out.println(arr2.getClass()); System.out.println(arr1.getClass() == arr2.getClass()); } /*執行結果: class java.util.ArrayList class java.util.ArrayList true */
編譯器承擔了全部的型別檢查工作。編譯器禁止某些泛型的使用方式,正是為了確保型別的安全性。
五、泛型的相容性設計
關於泛型的轉化相容設計:
程式碼如下:
//原始型別可以和泛型型別相互賦值 ArrayList<String> list1 = new ArrayList(); ArrayList list2 = new ArrayList<String>(); //引數型別不考慮引數的繼承關係,無論是向下轉型還是向上轉型 都會報錯 ArrayList<String> list3 = new ArrayList<Object>(); //編譯失敗 ArrayList<Object> list4 = new ArrayList<String>(); //編譯失敗 //不能建立陣列元素為引數型別的陣列 ArrayList<String>[] array = new ArrayList<String>[10]; //編譯失敗
六、萬用字元?
先看一個列印集合中所有元素的程式碼。
//不使用泛型時 void printCollection(Collection c) { Iterator i=c.iterator(); for (k=0;k < c.size();k++) { System.out.println(i.next()); } }
//使用泛型、、、 void printCollection(Collection<Object> c) { for (Object obj:c) { System.out.println(i.next()); } }
很容易發現,使用泛型的版本也只能接受元素型別為Object型別的集合如ArrayList<Object>();如果是ArrayList<String>,則會編譯時出錯。那麼如何改造新版本以便它能接受所有型別的集合呢?這個問題就可以通過使用萬用字元來解決。修改後的程式碼如下所示:
//使用萬用字元?,表示可以接收任何元素型別的集合作為引數 void printCollection(Collection<?> c) { for (Object obj:c) { System.out.println(i.next()); } }
這裡使用了萬用字元?指定可以使用任何型別的集合作為引數。讀取的元素使用了Object型別來表示,這是安全的,因為所有的類都是Object的子類。
但是還有另外一個問題:
如下程式碼所示,如果試圖往使用萬用字元?的集合中加入物件,就會在編譯時出現錯誤。需要注意的是,這裡不管加入什麼型別的物件都會出錯。這是因為萬用字元?表示該集合儲存的元素型別未知,可以是任何型別。往集合中加入元素需要是一個未知元素型別的子型別,正因為該集合儲存的元素型別未知,所以我們沒法向該集合中新增任何元素。唯一的例外是null,因為null是所有型別的子型別,所以儘管元素型別不知道,但是null一定是它的子型別。
Collection<?> c = new ArrayList<String>(); c.add(newObject()); //不管加入什麼物件都出錯,除了null外。 c.add(null); //OK
解決辦法:邊界萬用字元
1)?extends萬用字元
假定有一個畫圖的應用,可以畫各種形狀的圖形,如矩形和圓形等。為了在程式裡面表示,定義如下的類層次:
public abstract class Shape { public abstract void draw(Canvas c); } public class Circle extends Shape { private int x,y,radius; public void draw(Canvas c) { ... } } public class Rectangle extends Shape { private int x,y,width,height; public void draw(Canvasc) { ... } }
public void drawAll(List<?exends Shape> shapes) { for (Shapes:shapes) { s.draw(this); } }
這裡就又有個問題要注意了,如果我們希望在
List<?exends Shape> shapes 中加入一個矩形物件,如下所示:
shapes.add(0, new Rectangle()); //compile-time error
那麼這時會出現一個編譯時錯誤,原因在於:我們只知道shapes中的元素時Shape型別的子型別,具體是什麼子型別我們並不清楚,所以我們不能往shapes中加入任何型別的物件。不過我們在取出其中物件時,可以使用Shape型別來取值,因為雖然我們不知道列表中的元素型別具體是什麼型別,但是我們肯定的是它一定是Shape類的子型別。
2)?super萬用字元
這裡還有一種邊界萬用字元為?super。比如下面的程式碼:
List<Shape> shapes = new ArrayList<Shape>(); List<? super Cicle> cicleSupers = shapes; cicleSupers.add(new Cicle()); //OK, subclass of Cicle also OK cicleSupers.add(new Shape()); //ERROR
這表示cicleSupers列表儲存的元素為Cicle的超類,因此我們可以往其中加入Cicle物件或者Cicle的子類物件,但是不能加入Shape物件。這裡的原因在於列表cicleSupers儲存的元素型別為Cicle的超類,但是具體是Cicle的什麼超類並不清楚。但是我們可以確定的是隻要是Cicle或者Circle的子類,則一定是與該元素類別相容。
3) 邊界萬用字元總結
如果想從一個數據型別裡獲取資料,使用 ? extends 萬用字元
如果想把物件寫入一個數據結構裡,使用 ? super 萬用字元
如果既想存,又想取,那就別用萬用字元。
---------------------- ASP.Net+Unity開發、.Net培訓、期待與您交流! ----------------------