簡體   English   中英

Java泛型捕獲列表<?>

[英]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))這里列出lm是不同的。 但在上面的例子中, ll是相同的列表。 為什么編譯器不夠聰明才能看到它? 實施起來難嗎?

編輯 :我知道我可以通過輔助方法或使用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家伙采用原始類型,以便在本地處理此問題。 這是一個強有力的聲明,表明這可能應該由編譯器支持,但由於某種原因(例如,沒有時間正式指定這一點)只是沒有完成

編譯器會報告錯誤,因為通常情況下它無法判斷兩個表達式(在本例中為ll )是否引用相同的列表。

相關的,有點概括的問題:

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.

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