[英]Java Generics Capture List<?>
我正在查看Java Generics文檔並找到這段代碼,
public class WildcardError {
void foo(List<?> l) {
//This give a compile time error
l.set(0,l.get(0));
}
}
我可以理解我們從List<?>
中獲取一個元素並嘗試將其設置為另一個List<?>
。 所以編譯器會出錯。 我的問題是當2個列表不同l.set(0, m.get(0))
即l.set(0, m.get(0))
這里列出l
和m
是不同的。 但在上面的例子中, l
和l
是相同的列表。 為什么編譯器不夠聰明才能看到它? 實施起來難嗎?
編輯 :我知道我可以通過輔助方法或使用T
而不是?
來修復它?
。 只是想知道為什么編譯器不會為我做這件事。
在您的具體情況下,您可以明確解決此問題:
public class WildcardError {
<T> void foo(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
或者,如果您不想更改原始API,請引入委托幫助程序方法:
public class WildcardError {
void foo(List<?> l) {
foo0(l);
}
private <T> void foo0(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
不幸的是,編譯器無法推斷出“明顯的” <T>
類型。 我一直在想這個。 它似乎可以在編譯器中得到改進,因為每個通配符都可以非正式地轉換為未知的<T>
類型。 可能有一些原因可以解釋為什么這個被省略,也許這只是直覺,但在形式上是不可能的。
更新 :
注意,我剛剛看到了Collections.swap()
這種特殊實現:
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
JDK家伙采用原始類型,以便在本地處理此問題。 這是一個強有力的聲明,表明這可能應該由編譯器支持,但由於某種原因(例如,沒有時間正式指定這一點)只是沒有完成
List<?>
表示包含某些未知類型的元素的列表 ,因此當想要使用list.get(i)
獲取元素時,它將返回某種未知類型的對象 ,因此唯一有效的猜測將是Object
。 然后當使用list.set(index, list.get(index))
嘗試設置元素時list.set(index, list.get(index))
它會產生編譯時錯誤,因為如上所述, List<?>
只能包含一些未知類型 ,因此將Object
放入其中可能導致ClassCastException
。
Joshua Bloch的Effective Java,第2版,第28項:使用有界通配符增加API靈活性,這一點得到了很好的解釋。
這也稱為PECS
原理,在此Q / A中可以找到很好的解釋: 什么是PECS(Producer擴展消費者超級)? (請注意, List<?>
與List<? extends Object>
相同,但有少量例外)
在非專業術語中,應該使用List<?>
作為方法參數,僅在該方法中從中獲取元素 ,而不是在需要將元素放入列表時。 當需要同時放置和獲取他/她需要使用類型參數T
生成方法時,如Lukas Eder的答案(類型安全方式)或簡單地使用List<Object>
(不是類型安全方式)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.