2012-07-16 46 views
5

我一直在尋找在Java Generics documentation,發現這段代碼,Java泛型捕獲列表<?>

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))這裏的列表lm是不同的。但在上面的例子中,ll是相同的列表。爲什麼編譯器不夠聰明呢?難以實施嗎?

編輯: 我知道我可以通過一個輔助方法或使用T代替?的修復它。只是想知道爲什麼編譯器不會爲我做。

回答

4

編譯器報告一個錯誤,因爲通常無法識別兩個表達式(本例中爲ll)是否引用相同的列表。

相關,有些一概而論,問題:

+0

我一直想知道的正式理由很長一段時間。我個人認爲這是JLS中的一個缺點,因爲兩個'l'中的通配符在形式上是相同的。至少通過直覺......但是,可能在更復雜的代碼片段中,識別「相同」通配符變得更加困難 – 2012-07-16 07:53:14

+0

是的。有可能會有一些有趣的角落案例,這將迫使規格變得相當複雜。好問題,但。這是這個問題的一個普遍性。你應該將其作爲後續發佈。 – aioobe 2012-07-16 07:55:37

+1

OK,這是一個問題:http://stackoverflow.com/questions/11500385/how-does-the-jls-justify-that-wildcards-cannot-be-formally-used-within-methods – 2012-07-16 08:22:47

9

在特定情況下,可以明確地解決這個問題:

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>類型。可能有些原因爲什麼會被忽略,也許這只是直覺,但在形式上是不可能的。

UPDATE

注意,我剛看到這個奇特的實施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人求助於原始類型,爲了解決這個問題,在本地。這表明這可能應該由編譯器支持的強有力的聲明,但由於某種原因(如時間不夠正式指定此),只是沒有做

+1

是的,我知道其他解決方案。只是想知道爲什麼編譯器不能自己做這兩個列表是相同的。 – 2012-07-16 07:52:11

+1

是的,我也想知道。它會一直讓我從過去書寫數百無用代表團方法...: - /也許你應該更新你的問題,並要求從[JLS]一個具體的解釋(http://docs.oracle.com/ javase/specs/jls/se7/html/index.html),這就解釋了這一點。 – 2012-07-16 07:54:04

+0

感謝您編輯帖子:)。我在哪裏可以問問JLS? – 2012-07-16 08:07:39

2

List<?>指一些不知名的包含列表元素輸入,所以當一個人想使用它的元素使用list.get(i)它會返回某個未知類型的對象,所以唯一有效的猜測將是Object。然後當試圖使用list.set(index, list.get(index))設置元素時,它會產生編譯時錯誤,因爲如上所述List<?>只能包含一些未知類型,因此將Object置於其上可能會導致ClassCastException

這約書亞布洛赫的有效的Java解釋得非常好,第2版,第28項:使用有界通配符來提高API的靈活性

這也被稱爲PECS原則,很好的解釋都可以在此找到Q/A: What is PECS (Producer Extends Consumer Super)?(請注意:List<?>相同List<? extends Object>上略有不同)

在深入淺出而言,應該使用List<?>爲方法的參數只能得到元素從它內部的方法,而不是當需要把元素列表。當一個人需要兩個put和get他/她需要使用類型參數T在盧卡斯埃德的答案(類型安全的方式),或簡單地使用List<Object>(不是類型安全方式)兩種方法泛型化。