Java 泛型 泛型數組


Java 泛型 泛型數組

 @author ixenos

 

 

 

  • 先給結論
    • 不能(直接)創建泛型數組
    • 泛型數組實際的運行時對象數組只能是原始類型( T[]為Object[],Pair<T>[]為Pair[] ),而實際的運行時數組對象可能是T類型( 雖然運行時會擦除成原始類型 )
    • 一般解決方案:(泛型數組包裝器):使用ArrayList收集泛型數組對象的對象元素,如ArrayList<T>、ArrayList<Pair<String>>
      • 將獲得數組的行為,以及由泛型提供的編譯期的類型安全

 

  • 直接創建泛型數組不能通過編譯,而轉型對象數組通過編譯但是不能在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(); //能成功返回了!
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM