3 回答

TA貢獻1951條經驗 獲得超3個贊
第一碼
List<Integer>[] array = (List<Integer>[]) new Object[size];
第一個代碼失敗的原因是因為轉換并沒有改變數組的實際類型,它只是讓編譯器接受代碼是有效的。想象一下,如果您有另一個對底層對象數組的引用:
final int size = 2;
Object[] objectArr = new Object[size];
List<Integer>[] integerArr = (List<Integer>[]) objectArr; // Does not work
objectArr[0] = "foobar";
List<Integer> i = integerArr[0]; // What would happen ??
上面的代碼編譯得很好,因為你強制編譯器接受它的強制轉換。但是您已經可以看到為什么強制轉換在運行時工作會是一個問題:您最終會得到一個List<Integer>[]
現在包含一個 的String
,這是沒有意義的。所以語言不允許這樣做。
第二碼
E[] array = (E[]) new Object[size];
Java 中的泛型有點奇怪。由于各種原因,例如向后兼容性,泛型基本上被編譯器擦除并且(大部分)不會出現在編譯代碼中(類型擦除)。相反,它將使用一系列規則(JLS 規范)來確定代碼中應該使用哪種類型。對于基本的 unbouded 泛型;這種類型將是Object
。因此,假設沒有綁定E
,編譯器將第二個代碼更改為:
Object[] array = (Object[]) new Object[size];
因此,由于兩個數組在擦除后具有完全相同的類型,因此在運行時沒有問題,并且轉換基本上是多余的。
值得注意的是,這僅在E
不受限制的情況下才有效。例如,這將在運行時失敗并顯示ClassCastException
:
public static <E extends Number> void genericMethod() {
final int size = 5;
E[] e = (E[]) new Object[size];
}
那是因為Ewill be erased to Number,你會遇到和第一個代碼一樣的問題:
Number[] e = (Number[]) new Object[size];
在使用代碼時記住擦除很重要。否則,您可能會遇到代碼行為與您預期不同的情況。例如,下面的代碼編譯和運行沒有異常:
public static <E> void genericMethod(E e) {
final int size = 2;
Object[] objectArr = new Object[size];
objectArr[0] = "foobar";
@SuppressWarnings("unchecked")
E[] integerArr = (E[]) objectArr;
integerArr[1] = e;
System.out.println(Arrays.toString(integerArr));
System.out.println(e.getClass().getName());
System.out.println(integerArr.getClass().getName());
}
public static void main(String[] args) {
genericMethod(new Integer(5)); // E is Integer in this case
}
第三碼
List<Integer>[] array = (List<Integer>[]) new ArrayList[size];
與上面的情況類似,第三個代碼將被擦除為以下內容:
List[] array = (List[]) new ArrayList[size];
這沒問題,因為ArrayList
是List
.
第四碼
List<Integer>[] array = new ArrayList<Integer>[size];
以上不會編譯。規范明確禁止使用具有泛型類型參數的類型創建數組:
如果正在初始化的數組的組件類型不可具體化(第 4.7 節),則會出現編譯時錯誤。
具有不是無限通配符 ( ?
) 的泛型參數的類型不滿足可具體化的任何條件:
當且僅當滿足以下條件之一時,類型才可具體化:
它指的是非泛型類或接口類型聲明。
它是一種參數化類型,其中所有類型參數都是無限通配符(第 4.5.1 節)。
它是原始類型 (§4.8)。
它是原始類型(§4.2)。
它是一種數組類型 (§10.1),其元素類型是可具體化的。
它是一個嵌套類型,其中對于由“.”分隔的每個類型 T,T 本身是可具體化的。

TA貢獻1810條經驗 獲得超4個贊
雖然我沒有時間深入挖掘JLS
,但我可以暗示您要看得更遠(盡管每次我這樣做,都不是一次愉快的旅行)。
List<Integer>[] array = (List<Integer>[]) new Object[size];
這不會編譯,因為它們是可證明的不同類型(搜索JLS
這樣的概念)。簡而言之,編譯器“能夠”看到這些類型不可能與可能被強制轉換的類型相同,因此失敗。
另一方面:
array = (E[]) new Object[10];
這些不是可證明的不同類型;編譯器無法判斷這一定會失敗。這里的另一件事是,編譯器不會以任何形式或形狀強制轉換為泛型類型,您可以輕松地做這樣的事情(仍然可以編譯):
String s[][][] = new String[1][2][3]; array = (E[]) s; // this will compile, but makes little sense
第二點是類型擦除(又有JLS
)。
編譯代碼后,E[]
在運行時,是Object[]
(除非有界限,但這里不是這種情況),很明顯你可以把任何你想要的東西放進去。

TA貢獻1886條經驗 獲得超2個贊
Java 中數組和泛型之間的交互是混亂的,因為它們建立在不同的假設之上。Java 數組有運行時類型檢查,泛型只有編譯時類型檢查。
Java 通過結合編譯時和運行時檢查來實現類型安全。轉換繞過了大部分編譯時檢查,但仍然有運行時檢查。數組與其包含的元素類型具有本質上相同的類型兼容性規則。所以:
Object[] a = new String[size]; //ok, but be aware of the potential for an ArrayStoreException
String[] a = new Object[size]; //compile error
String[] a = (String[]) new Object[size]; //runtime error
當 Sun 決定將泛型添加到 Java 時,他們決定使用泛型的代碼應該在現有的 JVM 上運行,因此他們決定通過擦除來實現泛型。泛型類型只存在于編譯時,在運行時它們被普通類型取代。
所以在擦除之前我們有以下語句。
List<Integer>[] array = (List<Integer>[]) new Object[size];
E[] array = (E[]) new Object[size];
List<Integer>[] array = (List<Integer>[]) new List[size];
擦除后我們有。
List[] array = (List[]) new Object[size]; //run time error.
Object[] array = (Object[]) new Object[size]; //no error.
List[] array = (List[]) new List[size]; //no error.
E[] array = (E[]) new Object[size];應謹慎使用該結構,它違反了 Java 的正常類型模型,如果數組返回到非泛型上下文,將導致令人困惑的 ClassCastException 。不幸的是,通常沒有更好的選擇,因為類型擦除,泛型類型無法找出它的元素類型并構造正確類型的數組。
添加回答
舉報