12 泛型
12.1 為什么使用泛型
泛型程序設計(Generic programming):意味著編寫的代碼可以被很多不同類型的對象所重用。
類型參數(type parameters)
通配符類型(wildcard type) 可以將Manager添加到ArrayList<Employee>中,但不能把Employee添加到ArrayList<Manager>中。
?
12.2 定義簡單泛型類
一個泛型類(Generic class)就是具有一個或多個類型變量的類。
public class Pair<T, U>{...}
類型變量用大寫形式,且比較短。
?
在Java庫中,E表示集合的元素類型;K和V分別表示關鍵字和值的類型;T、U、S表示任意類型。
?
泛型類可看做普通類的工廠。
?
12.3 泛型方法
可以定義一個帶有類型參數的簡單方法,這個方法可以在普通類中,也可以在泛型類中。
類型變量T放在修飾符和返回類型之間。
當調用泛型方法時,在方法名前的尖括號中放入具體的類型。
也可以不放具體類型,編譯器會進行類型推斷。
class ArrayAlg
{public static <T> T getMiddle(T ... a){return a[a.length / 2];}
}String middle = ArrayAlg.<String>getMiddle( “John”, “Q”, “Public”);
String middle = ArrayAlg.getMiddle( “John”, “Q”, “Public”);
12.4 類型變量的限定
public static <T extends Comparable> T min(T[] ?a) { ... }
public static <T extends Comparable & Serializable> T min(T[] ?a) { ... }
?
限定中至多有一個類,且必須放在限定列表中的首位。
?
12.5 泛型代碼和虛擬機
虛擬機沒有泛型類型對象--所有對象都屬于普通類。
定義一個泛型類型時,都自動提供了一個相應的原始類型(raw type)。
原始類型的名字就是刪去類型參數后的泛型類型名。
擦除(erased)類型變量,替換為限定類型(無限定類型用Object)。
泛型方法同上。
?
這是與C++模板最大的區別,C++每個模板的實例化產生不同的類型,這一現象稱為“模板代碼膨脹”。
?
小結:
·虛擬機中沒有泛型,只有普通的類和方法;
·所有的類型參數都用它們的限定類型替換;
·橋方法被合成來保持多態;
·為保持類型安全性,必要時插入強制類型轉換。
?
橋方法位于聲明類型的泛型類中:
public void setSecond(Object second) { setSecond((Data) second)};
public Data getSecond{ return (Date) super.getSecond().clone();}
?
12.6 約束與局限性
1、不能用基本類型實例化類型參數;
?
2、運行時類型查詢只適用于原始類型。
虛擬機中的對象總有一個特定的非泛型類型,因此,所有的類型查詢只產生原始類型。
if (a instanceof Pair<String>) //ERROR
if (a instanceof Pair<T>) //ERROR
Pair<String> p = (Pair<String>) a; //WARNING--can only test that a is a Pair.
無論何時使用instanceof或涉及泛型類型的強制類型轉換表達式都會看到一個編譯器警告。
?
同理,getClass方法總是返回原始類型。
Pair<String> stringPair = ...;
Pair<Emloyee> employeePair = ... ;
if (stringPair.getClass() == employeePair.getClass()) //they are equal
//兩次調用getClass都將返回Pair.class
?
3、不能實例化參數化類型數組;
Pair<String>[] table = new Pair<String>[10]; // ERROR
ArrayList<Pair<String>> table = new ArrayList<Pair<String>>(); //RIGHT
4、向參數個數可變的方法傳遞一個泛型類型的實例:
public static <T> void addAll( Collection<T> coll, T ... ts)
實際上ts是一個數組,這違反了3,但此時規則有些放松,只會得到警告。
可用@SafeVarargs來消除警告。
或者@SuppressWarnings(“unchecked”)來抑制警告
?
5、不能實例化類型變量;
不能使用new T(...); ?new T[...]; ?T.class
可以這樣用
public static <T> Pair<T> makePair(Class<T> c1)
{try { return new Pair<>( c1.newInstance(), c1.newInstance())}catch (Exception ex) { return null;}
}
Class類本身就是泛型,String.class是一個Class<String>的實例。
?
6、禁止使用帶有類型變量的靜態域和方法;
?
7、不能拋出或捕獲泛型類的實例;
?
12.7 泛型類型的繼承規則
無論S與T有什么關系,Pair<S> Pair<T>都沒什么關系。
泛型類可以擴展或實現其他的泛型類。這一點與普通的類沒有什么區別。
?
12.8 通配符類型
? 通配符。也可以理解為占位符。
? extends E: 可以接收E類型或者E的子類型。上限
? super E: 可以接收E類型或者E的父類型。下限 ?用的比較少,見集合的比較器
?
Pair<? extends Employee> 子類型限定
Pair<? super Manager> 超類型限定
?
Pair<Manager>是Pair<? extends Employee>的子類型
Pair<Employ>是Pair<? super Manager>的子類型
?
12.9 反射和泛型
Class類是泛型的。
使用反射API可以確定:
·類型參數T
·子類型限定
·通配符參數
·超類型限定
·參數為泛型