簡體   English   中英

轉換列表<String>列出<Object>甚至列表<Integer>演員表

[英]Convert List<String> to List<Object> or even List<Integer> by cast

有一個問題How to Convert List<String> to List<Object>和許多關於List<P>List<C> with C extends P 的類似問題。

顯然,我們不能像這樣使用強制轉換:

List<String> list0 = Arrays.asList("abc", "xyz");
List<Object> list1 = (List<Object>) list0; //ERROR here

上述問題中接受的答案是:

List<Object> objectList = new ArrayList<Object>(stringList);

但是當我嘗試這段代碼時:

List<String> list0 = Arrays.asList("abc", "xyz");
List<Object> list1 = (List<Object>) (Object) list0;
System.out.println(list1);

甚至像這樣:

List<String> list0 = Arrays.asList("abc", "xyz");
List<Integer> list1 = (List<Integer>) (Object) list0;
System.out.println(list1);

它運行成功,所以我們可以間接投射! 我真的很懷疑這個! 有什么可以接受的理由嗎?

這是(或多或少)可以預料的。 Java 泛型只存在於編譯器中,而不存在於運行時。 編譯器檢查是否一切正常,然后刪除類型,以便只保留Object (或任何下限)。

如果在此過程中轉換為Object ,則不再直接將一種泛型類型轉換為另一種類型,並且編譯器無法驗證任何內容,因此這是允許的。 而且由於泛型類型在運行時不再存在,它也可以工作。 但是您可能會破壞使用該列表的代碼,因為您現在可以添加Object而不僅僅是String 當您僅枚舉列表時,以您正在執行的方式進行雙重轉換可能沒問題,但如果可能,我會避免在任何一種情況下都這樣做。 類型安全是一件好事。

隨着您對List<Integer>強制轉換,完全相同的事情正在發生。 它仍然有效的原因是因為您只是在打印列表(不確定是否用 Java 枚舉和寫入列表的內容,但如果是這樣,那么無論如何它都可能會在元素上調用toString() 。因為toString是一個Object上的方法無論是在Integer還是String上調用它都沒有區別。生成的字節碼是相同的。但是,如果您改為調用Integer方法,那么您肯定會在運行時崩潰,即使編譯器,如前所述,不能讓你免於做愚蠢的事情。

寫作:

List<Integer> list1 = (List<Integer>) (Object) list0;

相當於:

Object obj = (Object) list0;
List<Integer> list1 = (List<Integer>) obj;

就編譯器而言,將任何引用類型轉換為Object總是有效的(因為所有引用類型都是Object s)。 也允許第二次轉換,因為就編譯器而言, Object引用可以(在運行時)保存對任何引用類型的引用,因此它允許您可以轉換為任何類型(甚至是Set<Integer> ,它將通過編譯並且僅在運行時失敗)。

現在,在運行時,擦除泛型類型參數后,您的代碼變為:

List list0 = Arrays.asList("abc", "xyz");
List list1 = (List) (Object) list0;
System.out.println(list1);

兩種強制轉換在運行時都有效,因此您的代碼不會導致ClassCastException

Generic<Subclass>Generic<SuperClass>是不安全的,但大多數開發人員並不關心這一點。 查看以下示例類

class Fruit {
}
class Apple extends Fruit {
}

Apple總是可以用作Fruit的地方, Bowl<Apple>不能用作Bowl<Fruit> 如果我將Bowl<Apple>投射到Bowl<Fruit> ,我稍后可能會在水果碗中添加一個Orange ,而原來的蘋果碗將包含一個橙子。 例子:

Bowl<Apple> apples = new Bowl<Apple>();
...
Bowl<Fruit> fruit = (Bowl<Apple>) apples; // no exception
fruit.add(new Orange()); // no exception
...
Apple apple = apples.get(0); // no compiler warning, but exception

所以不允許強制轉換的目的可能是提醒用戶他可能正在做一些他不理解的事情。

然而編譯器允許我們將FruitApple 這是 Java 的一個限制(其他語言只允許安全轉換(通過類型檢查確保轉換是可能的)),但它沒有泛型轉換那么糟糕,因為異常是在進行非法轉換的位置拋出的- 使用泛型轉換時,即使在另一個類方法中也可能發生異常。

然而,在某些情況下,我們知道水果碗將只包含蘋果(將來也會),或者列表將始終包含字符串(例如,如果數據不可修改)。 在這種情況下,我更喜歡這個成語:

List<Object> list1 = (List<Object>) (List) list0;

List 和 List 不兼容。 但是你可以寫:

List<Object> list1 = (List<Object>)(List<?>)  list0;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM