亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

關于在 Java 中創建通用列表數組的錯誤

關于在 Java 中創建通用列表數組的錯誤

慕斯709654 2023-03-23 15:17:32
第一個代碼:List<Integer>[] array = (List<Integer>[]) new Object[size]; 它將給出以下異常:java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.util.List; ([Ljava.lang.Object; and [Ljava.util.List; are in module java.base of loader 'bootstrap')為什么這是錯誤的?我只是按照Effective Java Third Edition Page 132 的方法:第二個代碼:E[] array = (E[]) new Object[size];但是我發現以下代碼有效第三代碼:List<Integer>[] array = (List<Integer>[]) new List[size];我的問題:為什么第一個代碼是錯誤的,但Effective Java中建議使用第二個代碼?我有什么誤解嗎?例如:為什么下面的代碼運行良好,但第一個代碼是錯誤的?public class Test<E>{    E[] array;    public Test(){        array = (E[]) new Object[10];    }    public E set(E x){        array[0] = x;        System.out.println(array[0]);        return array[0];    }    public static void main(String[] args){        Test<List<Integer>> test = new Test<>();        List<Integer> list = new ArrayList<>();        list.add(1);        test.set(list);    }}誰能解釋為什么第三個代碼是正確的但下面的代碼是錯誤的?第四個代碼:List<Integer>[] array = new List<Integer>[size];
查看完整描述

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];

這沒問題,因為ArrayListList.

第四碼

List<Integer>[] array = new ArrayList<Integer>[size];

以上不會編譯。規范明確禁止使用具有泛型類型參數的類型創建數組:

如果正在初始化的數組的組件類型不可具體化(第 4.7 節),則會出現編譯時錯誤。

具有不是無限通配符 ( ?) 的泛型參數的類型不滿足可具體化的任何條件

當且僅當滿足以下條件之一時,類型才可具體化:

  • 它指的是非泛型類或接口類型聲明。

  • 它是一種參數化類型,其中所有類型參數都是無限通配符(第 4.5.1 節)。

  • 它是原始類型 (§4.8)。

  • 它是原始類型(§4.2)。

  • 它是一種數組類型 (§10.1),其元素類型是可具體化的。

  • 它是一個嵌套類型,其中對于由“.”分隔的每個類型 T,T 本身是可具體化的。


查看完整回答
反對 回復 2023-03-23
?
慕萊塢森

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[](除非有界限,但這里不是這種情況),很明顯你可以把任何你想要的東西放進去。


查看完整回答
反對 回復 2023-03-23
?
MM們

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 。不幸的是,通常沒有更好的選擇,因為類型擦除,泛型類型無法找出它的元素類型并構造正確類型的數組。


查看完整回答
反對 回復 2023-03-23
  • 3 回答
  • 0 關注
  • 177 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號