讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議94:不能初始化泛型參數和數組 >

建議94:不能初始化泛型參數和數組

泛型類型在編譯期被擦除,我們在類初始化時將無法獲得泛型的具體參數,比如這樣的代碼:


class Foo<T>{

private T t=new T();

private TtArray=new T[5];

private List<T>list=new ArrayList<T>();

}


這段代碼有什麼問題呢?t、tArray、list都是類變量,都是通過new聲明了一個類型,看起來非常相似啊!但這段代碼是編譯通不過的,因為編譯器在編譯時需要獲得T類型,但泛型在編譯期類型已經被擦除了,所以new T()和new T[5]都會報錯(可能有讀者疑惑了:泛型類型可以擦除為頂級類Object,那T類型擦除成Object不就可以編譯了嗎?這樣也不行,泛型只是Java語言的一部分,Java語言畢竟是一個強類型、編譯型的安全語言,要確保運行期的穩定性和安全性就必須要求在編譯器上嚴格檢查)。可為什麼new ArrayList<T>()卻不會報錯呢?

這是因為ArrayList表面是泛型,其實已經在編譯期轉型為Object了,我們來看一下ArrayList的源代碼就清楚了,代碼如下:


public class ArrayList<E>extends AbstractList<E>

implements List<E>,RandomAccess, Cloneable, java.io.Serializable

{

//容納元素的數組

private transient ObjectelementData;

//構造函數

public ArrayList(){

this(10);

}

//獲得一個元素

public E get(int index){

RangeCheck(index);

//返回前強制類型轉換

return(E)elementData[index];

}

}


注意看elementData的定義,它容納了ArrayList的所有元素,其類型是Object數組,因為Object是所有類的父類,數組又允許協變(Covariant),因此elementData數組可以容納所有的實例對象。元素加入時向上轉型為Object類型(E類型轉為Object),取出時向下轉型為E類型(Object轉為E類型),如此處理而已。

在某些情況下,我們確實需要泛型數組,那該如何處理呢?代碼如下:


class Foo<T>{

//不再初始化,由構造函數初始化

private T t;

private TtArray;

private List<T>list=new ArrayList<T>();

//構造函數初始化

public Foo(){

try{

Class<?>tType=Class.forName("");

t=(T)tType.newInstance();

tArray=(T)Array.newInstance(tType,5);

}catch(Exception e){

e.printStackTrace();

}

}

}


此時,運行就沒有任何問題了。剩下的問題就是怎麼在運行期獲得T的類型,也就是tType參數,一般情況下泛型類型是無法獲取的,不過,在客戶端調用時多傳輸一個T類型的class就會解決問題。

類的成員變量是在類初始化前初始化的,所以要求在初始化前它必須具有明確的類型,否則就只能聲明,不能初始化。