泛型Generics
所謂泛型,就是變量類型的參數化。
泛型是JDK1.5中一個最重要的特征。通過引入泛型,我們將獲得編譯時類型的安全和運行時更小的拋出ClassCastException的可能。
在JDK1.5中,你可以聲明一個集合將接收/返回的對象的類型。
使用泛型時如果不指明參數類型,即泛型類沒有參數化,會提示警告,此時類型為Object。
為什么使用泛型
使用泛型的典型例子,是在集合中的泛型使用。
在使用泛型前,存入集合中的元素可以是任何類型的,當從集合中取出時,所有的元素都是Object類型,需要進行向下的強制類型轉換,轉換到特定的類型。
比如:
List myIntList = new LinkedList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = (Integer) myIntList.iterator().next(); // 3
第三行的這個強制類型轉換可能會引起運行時的錯誤。
泛型的思想就是由程序員指定類型,這樣集合就只能容納該類型的元素。
使用泛型:
List<Integer> myIntList = new LinkedList<Integer>(); // 1' myIntList.add(new Integer(0)); // 2' Integer x = myIntList.iterator().next(); // 3'
將第三行的強制類型轉換變為了第一行的List類型說明,編譯器會為我們檢查類型的正確性。這樣,代碼的可讀性和健壯性也會增強。
泛型使用基礎
例如:
public interface List <E> { void add(E x); Iterator<E> iterator(); } public interface Iterator<E> { E next(); boolean hasNext(); }
尖括號中包含的是形式類型參數(formal type parameters),它們就如同一般的類型一樣,可以在整個類的聲明中被使用。
當類被使用時,會使用具體的實際類型參數(actual type argument)代替。
比如前面的例子中的List<Integer>,那么所有的E將會被Integer類型所代替。
泛型類型參數只能被類或接口類型賦值,不能被原生數據類型賦值,原生數據類型需要使用對應的包裝類。
形式類型參數的命名:盡量使用單個的大寫字母(有時候多個泛型類型時會加上數字,比如T1,T2),比如許多容器集合使用E,代表element(元素),Map中用K代表鍵keys,V代表值。
泛型容器的實現討論
不能用new的形式來創建一個泛型數組。
如下:
public class SimpleCollection<T> { private T[] objArr; private int index = 0; public SimpleCollection() { //Error: Cannot create a generic array of T objArr = new T[10]; } }
會報錯。
如何創建一個數組讓它接受所有可能的類型呢?
public class SimpleCollection<T> { private T[] objArr; private int index = 0; public SimpleCollection() { //Error: Cannot create a generic array of T //objArr = new T[10]; //Warning: Unchecked cast from Object[] to T[] objArr = (T[]) new Object[10]; } }
這個形式雖然可以做到,但是會產生一個警告。
查看ArrayList中的實現,可以發現它是使用了一個Object類型的數組:
private transient Object[] elementData;
在取出的時候(get方法中)使用了類型轉換:
(E) elementData[index];
泛型和子類
List<String> ls = new ArrayList<String>(); // 1 List<Object> lo = ls; // 2
一個String類型的List是一個Object類的List嗎?
不可以,Java編譯器將會在第二行產生一個編譯錯誤,因為它們的類型不匹配。
這樣就避免了如果lo引入加入Object類型的對象,而ls引用試圖將其轉換為String類型而引發錯誤。所以編譯器阻止了這種可能。
繼承泛型類別
直接用例子說明:
父類:
public class Parent<T1,T2> { private T1 foo1; private T2 foo2; public T1 getFoo1() { return foo1; } public void setFoo1(T1 foo1) { this.foo1 = foo1; } public T2 getFoo2() { return foo2; } public void setFoo2(T2 foo2) { this.foo2 = foo2; } }
子類繼承父類:
public class Child<T1, T2, T3> extends Parent<T1, T2> { private T3 foo3; public T3 getFoo3() { return foo3; } public void setFoo3(T3 foo3) { this.foo3 = foo3; } }
實現泛型接口
見例子:
泛型接口:
public interface ParentInterface<T1,T2> { public void setFoo1(T1 foo1); public void setFoo2(T2 foo2); public T1 getFoo1(); public T2 getFoo2(); }
子類實現泛型接口:
public class ChildClass<T1,T2> implements ParentInterface<T1, T2> { private T1 foo1; private T2 foo2; @Override public void setFoo1(T1 foo1) { this.foo1 = foo1; } @Override public void setFoo2(T2 foo2) { this.foo2 = foo2; } @Override public T1 getFoo1() { return this.foo1; } @Override public T2 getFoo2() { return this.foo2; } }
參考資料:
聖思園張龍老師Java SE視頻教程。
The Java Tutorials : Lesson: Generics (Updated)
http://docs.oracle.com/javase/tutorial/java/generics/index.html
Lesson: Generics
http://docs.oracle.com/javase/tutorial/extra/generics/index.html