Java 泛型 泛型數組

  • 直接建立泛型數組不能經過編譯,而轉型對象數組經過編譯可是不能在JVM運行
    • 複製代碼
      public class ArrayOfGeneric{
          static Generic<Integer>[] gia;
          @SupperssWarnings("unchecked")
          public static void main(String[] args){
              gia = (Generic<Integer>[])new Generic[100]; // 經過類型轉換匿名對象
              //! gia[0] = new Object(); //編譯不經過,不能(直接)建立泛型數組實例
          }
      }
      複製代碼
    • 問題在於數組將跟蹤他們的實際類型,而這個類型是在數組被建立時肯定的,所以,即便gia已經被轉型爲Generic<Integer>[],但這個信息只存在於編譯期(而且若是沒有@SuppressWarning("unchecked")註解,將獲得這個轉型的警告)。在運行時,它仍舊是Object數組
    • 所以,成功建立泛型數組的惟一方式就是建立一個被擦出類型的新數組,而後對其轉型(並且是在運行時轉型)
      •  直接對整個數組強制轉型,在編譯時依舊會被擦除掉類型!因此應該在運行時轉型,而這時最好的辦法就是使用一個泛型數組包裝器,維護一個原始類型的數組,經過數組入口方法進行元素編譯期的類型安全檢測(對應返回值)和強制類型轉換(對於運行時不重要),從而保證類型安全。

 

    • 對整個數組強制轉型的例子(錯誤方法)
    • 複製代碼
      public class GenericArray<T> {
          private T[] array;
          @SupperessWarning("unchecked")
          public GenericArray(int sz) {
              array = (T[]) new Object[sz];
          }
          public void put(int index, T item) {
              array[index] = item;
          }
          public T get(int index) { return array[index]; }
          public T[] rep() { return array; } //應該在運行時出口作文章
          public static void main (String[] args){
              GenericArray<Integer> gai = new GenericArray<Integer>(10);
              // Integer[] ia = gai.rep(); //ClassCastException
              Object[] oa = gai.rep(); //只能返回對象數組類型爲Object[]
      複製代碼
      • 實際的運行時對象數組是Object[],而實際的運行時數組對象多是T類型。

 

    • 所以,應該在運行時,數組對象的出口作轉型輸出,入口方法在編譯期已實現類型安全,因此出口方法能夠放心強制類型轉換,保證成功。以下
      • 複製代碼
        public class GenericArray2<T> {
            private Object[] array;  //維護Object[]類型數組
            @SupperessWarning("unchecked")
            public GenericArray2(int sz) {
                array = new Object[sz];
            }
            public void put(int index, T item) {
                array[index] = item;
            }
            public T get(int index) { return (T)array[index]; }//數組對象出口強轉
            public T[] rep() { return (T[])array; } //運行時不管怎樣都是Object[]類型 
            public static void main (String[] args){
                GenericArray<Integer> gai = new GenericArray<Integer>(10);
                // Integer[] ia = gai.rep(); //依舊ClassCastException
                Object[] oa = gai.rep(); //只能返回對象數組類型爲Object[]
                gai.put(0,11);
                System.out.println(gai.get(0)); // 11 ,出口成功轉型
            }
        }
        複製代碼

         

  •  經過反射在運行時構出實際類型爲type[]的對象數組,避免了類型擦除,從而轉換成功,無ClassCastException
複製代碼
import java.lang.reflect.*; 

public class GenericArrayWithTypeToken<T> {
    private T[] array;
    @SuppressWarning("unchecked")
    public GenericArrayWithTypeToken(Class<T> type, int sz) {
        array = (T[]) Array.newInstance(type, sz);//經過反射在運行時構出實際類型爲type[]的對象數組,避免了類型擦除,從而轉換成功,無ClassCastException
    }
    public void put(int index, T item){
        array[index] = item;
    }
    public T get(int index) { return array[index]; }
    public T[] rep() { return array; }  //能成功返回了~
    public static void main(String[] args) {
        GenericArrayWithTypeToken<Integer> gawtt = new GenericArrayWithTypeToken<>(Integer.class, 10);
        Integer[] ia = gawtt.rep(); //能成功返回了!
    }
}

  • 結論
    • 不能(直接)建立泛型數組
    • 泛型數組實際的運行時對象數組只能是原始類型( T[]爲Object[],Pair<T>[]爲Pair[] ),而實際的運行時數組對象多是T類型( 雖然運行時會擦除成原始類型 )
    • 通常解決方案:(泛型數組包裝器):使用ArrayList收集泛型數組對象的對象元素,如ArrayList<T>、ArrayList<Pair<String>>
      • 將得到數組的行爲,以及由泛型提供的編譯期的類型安全
相關文章
相關標籤/搜索