泛型類型在編譯期被擦除,我們在類初始化時將無法獲得泛型的具體參數,比如這樣的代碼:
class Foo<T>{ //private T t =new T();//報錯Cannot instantiate the type T //private T[] tArray= new T[5];//報錯Cannot create a generic array of T 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的源代碼就清楚了.
1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ 3 /** 4 * The array buffer into which the elements of the ArrayList are stored. 5 * The capacity of the ArrayList is the length of this array buffer. 6 */ 7 private transient Object[] elementData; 8 /** 9 * Constructs an empty list with an initial capacity of ten. 10 */ 11 public ArrayList() { 12 this(10); 13 } 14 15 /** 16 * Returns the element at the specified position in this list. 17 * 18 * @param index index of the element to return 19 * @return the element at the specified position in this list 20 * @throws IndexOutOfBoundsException {@inheritDoc} 21 */ 22 public E get(int index) { 23 rangeCheck(index); 24 25 return elementData(index); 26 } 27 E elementData(int index) { 28 return (E) elementData[index]; 29 } 30 }
注意看elementData定義,它容納了ArrayList的所有元素,其類型是Object數組,因為Object是所有類的父類,數組又允許協變(Covariant),因此elementData數組可以容納所有的實例對象.
元素加入時向上轉型為Object類型(E類型轉變為Object),取出時向下轉型為E類型(Object轉為E類型).
在某些情況下,我們確實需要泛型數組,怎么處理?
1 import java.lang.reflect.Array; 2 import java.util.ArrayList; 3 import java.util.List; 4 5 public class Client { 6 public static void main(String[] args) { 7 8 9 } 10 } 11 12 class Foo<T>{ 13 //不再初始化,由構造函數初始化 14 private T t; 15 private T[] tArray; 16 private List<T> list= new ArrayList<T>(); 17 //構造函數初始化 18 public Foo(){ 19 try { 20 Class<?> tType = Class.forName(""); 21 t = (T)tType.newInstance(); 22 tArray = (T[])Array.newInstance(tType,5); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 27 } 28 }
此時運行就沒有任何問題了,剩下的問題就是怎么在運行期獲得T的類型.也就是tType參數.一般情況下泛型類型是無法獲取的,不過在客戶端調用時多傳輸一個T類型的class就會解決問題.
類的成員變量是在類初始化前初始化的,所以要求在初始化前它必須具有明確的類型.否則就只能聲明,不能初始化.