簡體   English   中英

如何使用帶有通配符聲明的泛型類?

[英]How can I use a generic class with wildcard declaration?

我的班級中有以下成員:

List<? extends SomeObject> list;

當我嘗試做:

list.add(list.get(0));

我得到:

Test.java:7: error: no suitable method found for add(CAP#1)
        list.add(list.get(0));
            ^
    method Collection.add(CAP#2) is not applicable
      (argument mismatch; Object cannot be converted to CAP#2)
    method List.add(CAP#2) is not applicable
      (argument mismatch; Object cannot be converted to CAP#2)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends Object from capture of ? extends Object
    CAP#2 extends Object from capture of ? extends Object

我的問題是雙重的:

  1. 為什么不編譯? 為什么我不能將get()的結果傳遞給add()

  2. 我怎樣才能在不訴諸鑄造的情況下以另一種方式實現這一目標?


我知道在帶有<T extends SomeObject>的方法中,我不能只說:

T someObject = list.get(0);
list.add(someObject);

因為我的 T 可能是另一個擴展名而不是? 延期。

我也明白我不能說:

List<? extends SomeObject> list1;
List<? extends SomeObject> list2;
list1.add(list2.get(0));

但是由於 add 和 get 應該在list.add(list.get(0))使用相同的泛型類型,我不明白為什么編譯器不接受它。


我真正需要的是

[something of type T where T is whatever was used to instantiate list] someObject = list.get(0);
list.add(someObject);

以便我以后可以

list.add(someObject);

我認為我不應該為我的整個班級做模板來實現這一點,對嗎?

class MyClass<T extends SomeObject> {
List<T> list;

然后是一個方法

T someObject = list.get(0);

當然有效,但搞砸了我代碼的其他部分。

所以第一個問題是為什么這不起作用,第二個問題是最好的解決方法是什么?

我的問題是雙重的,為什么我不能這樣做:

 list.add(list.get(0));

因為編譯器不夠聰明,無法知道您將list某些內容重新添加到list 編譯器不認為list.get(0)一旦被評估就與list有任何關系:它只是類型的“某種表達式” ? extends SomeObject ? extends SomeObject

要解決此問題,請添加一個具有自己的類型變量的方法:

private <T> void addFirst(List<T> list) {
  list.add(list.get(0));
}

並替換原來的list.add(list.get(0)); 調用這個:

addFirst(list);

這只在方法上定義了一個類型變量,不需要在類外可見,所以不需要類級別的類型變量。


也許值得指出的是,這類似於Collections.swap方法:使用set而不是add ,但是,從泛型的角度來看,這是同一件事:

@SuppressWarnings({"rawtypes", "unchecked"})
public static void swap(List<?> list, int i, int j) {
    // instead of using a raw type here, it's possible to capture
    // the wildcard but it will require a call to a supplementary
    // private method
    final List l = list;
    l.set(i, l.set(j, l.get(i)));
}

這采用了一種技術上類型安全的方法,並且確實避免了強制轉換; 但這有點粗糙,因為它使用原始類型。

我想這只是出於向后兼容的原因。 如果有機會再次編寫它,您可以像上面的addFirst方法一樣定義一個類型變量。

當使用通配符時,我們應該遵循Java 泛型和集合中引入The Get and Put Principle

Get 和 Put 原則:當你只從結構中獲取值時使用extends通配符,當你只將值放入結構時使用super通配符,並且當你同時獲取和放置時不要使用通配符。

在您的情況下,不要使用通配符,因為您既從列表中獲取元素,又將元素放入列表。

暫無
暫無

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

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