簡體   English   中英

我可以在列表聲明中使用泛型通配符嗎?

[英]Can I use generics wildcard in List declaration?

考慮以下代碼:

class Super {}
class Sub extends Super {}
class Test {
    public static void main(String[] args) {
        List<? extends Super> list = new ArrayList<Sub>(); //1
        list.add(new Sub()); //2
    }
}

第1行編譯成功,但第2行編譯失敗:

The method add(capture#2-of ? extends Super) in the type List<capture#2-of ? extends Super> is not applicable for the arguments (Sub)

我的問題是:
1)為什么第1行成功編譯?
2)第1行是聲明列表(或其他集合)的一種良好做法嗎?
3)為什么在第1行中將list聲明為Sub類型后,第2行為什么編譯失敗?
4)Eclipse的自動完成功能說,列表中現在僅允許“ null”元素。 為什么?

非常感謝!

1)為什么第1行成功編譯?

第一行進行編譯,因為List<Sub>List<? extends Super>子類List<? extends Super> List<? extends Super> ,並且只有在List不允許您向其添加任何新成員的情況下才可以。

? 意味着您並不完全知道它是List<Sub>List<Sub1> ,因此允許將新元素添加到列表中是不安全的,因此也不允許這樣做。

2)第1行是聲明列表(或其他集合)的一種良好做法嗎?

如果您已經知道它將成為List<Sub>那么我找不到任何用處,但是當您將List傳遞給其他類(如Utilities)時,會使用通配符。

3)為什么在第1行中將list聲明為Sub類型后,第2行為什么編譯失敗?

因為正如我已經解釋的那樣,當您不知道確切類型時,將任何元素添加到列表都是不安全的。

4)Eclipse的自動完成功能說,列表中現在僅允許“ null”元素。 為什么?

因為null是每種引用類型,所以這就是為什么您可以為任何對象分配null值的原因。

使用泛型時,請務必記住Josh Bloch的PECS(生產者擴展消費者超級用戶)規則

好的參考文獻:

如第1行所示,捕獲聲明在方法參數中很有用。 請參見Collection.addAll(Collection<? extends E>) 如果您需要帶有擴展Super的列表,只需使用List<Super>

您已將list聲明為,可以將其分配給Super類的任何子類型。 因此,分配一個Sub type列表就可以了,Compiler允許進行編譯。 但這並不意味着您可以在其中添加特定的對象類型。

<? extends Super> <? extends Super>並非意味着,您可以添加Super的任何子類型。 這意味着,您可以為其分配任何子類型集合。

1)為什么第1行成功編譯?

您基本上將列表定義為包含擴展(或為Super )的任何類型的元素,即,編譯器知道該列表中的每個元素至少應具有Super的屬性。

由於SubSuper的子類,因此任何僅包含Sub元素的列表也滿足所有元素都是Super實例的要求, List<? extends Super> list = new ArrayList<Sub>(); List<? extends Super> list = new ArrayList<Sub>(); 是正確的。

2)第1行是聲明列表(或其他集合)的一種良好做法嗎?

作為取決於個人風格的局部變量,恕我直言。 以這種方式聲明參數(或實例/靜態變量)時,通常不僅是好的樣式,而且也是必需的。

考慮一種迭代數字集合並返回總和的方法。 您可以將參數聲明為Collection<Number>但是如果沒有討厭的轉換,就無法傳遞Collection<Integer> 如果參數聲明為Collection<? extends Number> Collection<? extends Number>您可以傳遞Collection<Integer>

3)為什么在第1行中將list聲明為Sub類型后,第2行為什么編譯失敗?

原因是編譯器不知道列表中元素的確切類型。 Super列表還是Sub列表?

List<? extends Number> list為例 List<? extends Number> list 您不知道您是否具有List<Number>List<Double>List<Integer> ,因此無法確定list.add( new Integer(1) ); 會沒事的。 編譯器就是這樣看的。

4)Eclipse的自動完成功能說,列表中現在僅允許“ null”元素。 為什么?

我不得不在這里猜測,但是將null添加到列表中就可以了,因為無論實際列表聲明的是哪種類型,您都可以始終將null轉換為該類型。

1)為什么第1行成功編譯?

您將list聲明為“源自Super的事物的列表”。 您為其分配一個Sub列表。 Sub是“源自Super東西”。

2)第1行是聲明列表(或其他集合)的一種良好做法嗎?

不可以。通配符用於功能參數。 局部變量應在其泛型參數中盡可能具體,以避免出現您所面臨的問題。

3)為什么在第1行中將list聲明為Sub類型后,第2行為什么編譯失敗?

謬論。 list被聲明為具有“源自Super元素”的元素,而不是您聲稱的Sub 而且您不能將Sub添加到列表中,因為“東西”可能不是Sub 它可能是Sub2 ,並且添加等同於此非法分配:

class Super {}
class Sub extends Super {}
class Sub2 extends Super {}
Sub2 s = new Sub();

此處的主要誤解似乎是您認為通配符會在賦值時被替換。 不是。 它仍然是通配符,並且僅進行兼容性檢查。

4)Eclipse的自動完成功能說,列表中現在僅允許“ null”元素。 為什么?

null是唯一具有任何可能引用類型的值,因此無論通配符代表什么,都與列表兼容。

暫無
暫無

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

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