[英]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
我的問題是雙重的:
為什么不編譯? 為什么我不能將get()
的結果傳遞給add()
?
我怎樣才能在不訴諸鑄造的情況下以另一種方式實現這一目標?
我知道在帶有<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.