泛型程序設計 :能夠被不少不一樣類型的對象所重用。比那些直接使用Object變量,而後強制類型轉換的代碼具備跟好的安全性和可讀性。java
使用類型參數能夠將須要使用的類型,提早聲明數組
ArrayList<String> newlist = new ArrayList<String>();複製代碼
使用類型參數能夠告知這個類適用於什麼類型,當調用對應的get()方法的時候,不須要進行強制類型轉換,編譯器本事就知道其對應的類型。安全
當實現一個泛型的時候很是不容易,由於你須要知道這個這個類對應的全部用途及其類型,因此java提供了通配符類型,來解決這個問題。
bash
類型變量使用大寫形式,且比較短,這是很常見的。在java庫中,使用變量E表示集合的元素類型,K和V分別表示表的關鍵字和值的類型。T(須要時還能夠用臨近的字母U和S)表示類型。dom
泛型類,就是指具備一個或者多個類型變量,也就是說這個類適應這幾種類型,對於以後在那類來講,咱們只關注泛型,而不會爲數據村吃的細節煩惱。ui
使用類型變量T,用<>括起來,放在類名後面。這個泛型能夠有多個類型變量,如<T,U>this
可使用類定義的類型變量指定類中屬性和方法的類型。spa
public class Pari<T> {
private T first;
private T second;
public Pari(){
first = null;
second = null;
}
public Pari(T first,T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
public void setFirst(T value){
first = value;
}
public void setSecond(T value){
second = value;
}
}
複製代碼
Pair類引入了一個類型變量T,用尖括號(<>) 括起來, 並放在類名的後面;
其實泛型類能夠看作是普通類的工廠。設計
泛型方法既能夠在普通類中,也能夠在泛型類中,定義方式是在方法名前加<T> T,說明該方法是泛型方法code
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 ");複製代碼
double middle = ArrayAlg.getMiddle(3.14, 0, 1729);1複製代碼
編譯器將會自動打包參數爲 1個 Double 和 2個Integer 對象,然後尋找這些類的共同超類型。
有的時候,好比對於特定的方法執行特定的操做,可是該操做不適用於一些類型,這時能夠對類型變量T設置限定,可使其集成特別的類或者接口(沒錯,在這裏對於接口也是使用繼承,由於使用extends更加接近子類的意思)
一個類型變量或通配符能夠有多個限定,限定類型用「」&「」 分隔,而用逗號用來分隔類型變量。在java繼承中,能夠根據須要擁有多個接口超類型,但限定中至多有一個類。若是用一個類做爲限定,但必須是第一個。
好比:T extends Comparable & Srializable
public static <T extends Comparable> Pari<T> getMinMax(T[] word){
if(word == null || word.length == 0)
return null;
T min = word[0];
T max = word[0];
for(int i=1;i<word.length;i++){
if(word[i].compareTo(max) > 0)
max = word[i];
if(word[i].compareTo(min) < 0)
min = word[i];
}
return new Pari<T>(min,max);
}
複製代碼
JVM中沒有泛型,只有普通的類和方法
全部的類型參數都是用他們的限定類型轉換(若是沒有類型參數,則使用Object類型),這個過程稱爲擦除(erased),擦除類型變量,並替換爲限定類型
有時爲保持類型安全性,須要插入強制類型轉換
不能用類型參數來代替基本類型。就是沒有Pair<double>,只有Pair<Double>。固然主要是緣由是類型擦除。擦除以後,Pair類含有Obkect類型的域,而Object不能存儲double的值。
虛擬機中的對象總有一個特定的非泛型類型。所以,全部類型查詢只產生原始類型。
如:if(a instanceof Pari<String>)是錯誤的,由於只能查詢原始類型,即Pari,if(a instanceof Pari<T>)是錯誤的
又如:
Pari<String> pari1 = (Pari<String>) a
不管什麼時候使用instanceod或者設計泛型類型的強制類型轉換表達式都會看到一個編譯器警告。
一樣道理,getClass方法老是返回原始類型。
if (pari1.getClass() == pari2.getClass())返回true,由於兩次getClass()都是返回Pari.class
public class GenericException <T> extends Exception {...}
這種泛型類擴展子Throwable是不合法的,不能經過編譯器。
public static <T extends Throwable> void doWork(Class<T> t) {
try {
// do work...
} catch (T e) {
e.printStackTrace();
}
} // 錯誤 複製代碼
而這個是合法的
public static <T extends Throwable> void doWork(T t) throws T {
try {
// do work...
} catch (Throwable e) {
e.printStackTrace();
throw t;
}
}// 正確 複製代碼
java 異常處理的一個基本原則是,必須爲全部已檢查異常提供一個處理器。不過能夠利用泛型消除這個限制。
不能使用像new<T>(...),new t[...]或T.class這樣的表達式中的類型變量。例如,下面的Pair<T>構造器就是非法的:
public Pair()
{
first =new T();
second=new T ();
}//ERROR
複製代碼
類型擦除將T改變成Object。並且本意確定不但願調用newObject(),可是能夠經過反射調用Class.newInstance方法來構造泛型對象。
遺憾的是T.class在Java中也是不被支持使用的,因此一種彌補的方式,傳入一個類型闡述爲T的Class對象,如Class<T>
public static <T> Couple<T> createInstance(Class<T> clazz) {
try {
return new Couple<T>(clazz.newInstance(), clazz.newInstance());
} catch (Exception e) {
return null ;
}
}
複製代碼
初學者對Java反射不熟悉不用着急,這裏只要知道不能實例化類型參數便可,同理,不能實例化一個泛型數組,如
public static <T> T[] maxTwo(T[] values) {
T[] array = new T[2];
} // 錯誤
複製代碼
泛型構建數組是不合法的,由於這麼構建在擦除以後構造的永遠是new Object[2],這不是咱們所但願的結果。並且這樣會致使一些運行時錯誤。爲了更進一步說明隱患問題,來看看下面代碼:
public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
Object[] result = new Object[2];
return (T[]) result; // Type safety: Unchecked cast from Object[] to T[]
}
複製代碼
這種方式會產生變異警告:Object[]轉換爲T[]是沒有被檢查的。咱們來試一試這個調用: maxTwo(new String[] { "5", "7" , "9" });,運行後,發生了類型轉換異常,由於方法在調用的時候將Object[]轉換爲String[],失敗的類型轉化。怎麼解決呢?一樣這裏可使用Java發射來解決:
public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
// Type safety: Unchecked cast from Object[] to T[]
return (T[]) Array.newInstance(array.getClass().getComponentType(), 2) ;
}
複製代碼
不能在靜態域或方法中引用類型變量。
public class Singleton<T>
{
public static T singleInstacne;//ERROR
public static T getSingleInstance();//ERROR
{
if(singleInstacne==null)
return singleInstance;
}
}複製代碼
若是這個程序可以運行,就能夠聲明一個Singleton<Random>共享隨機數生成器,聲明一個Singleton<JFileChooser>共享文件選擇器對話框。可是,這個程序沒法工做。類型擦除以後,只剩下Singleton類,他只包含一個singleInstance域。
public class NameClash<T> {
public boolean equals(T value) {
return false ;
}
}
複製代碼
class Calendar implements Comparable<Calendar> {...}
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> {...} 複製代碼