2013-02-28 106 views
6

因此,我正在閱讀泛型以重新熟悉這些概念,特別是在涉及通配符的地方,因爲我很少使用它們或碰到它們。從我所做的閱讀中,我無法理解他們爲什麼使用通配符。我不斷遇到的一個例子如下。Java泛型:通配符

void printCollection(Collection<?> c) { 
    for (Object o : c){ 
    System.out.println(o); 
    } 
} 

爲什麼你會不會寫爲:

<T> void printCollection(Collection<T> c) { 
    for(T o : c) { 
     System.out.println(o); 
    } 
} 

從Oracle網站又如:

public static double sumOfList(List<? extends Number> list) { 
    double s = 0.0; 
    for (Number n : list) 
     s += n.doubleValue(); 
    return s; 
} 

這是爲什麼不寫成

public static <T extends Number> double sumOfList(List<T> list) { 
    double s = 0.0; 
    for (Number n : list) 
     s += n.doubleValue(); 
    return s; 
} 

上午我錯過了什麼?

+0

可能[duplicate](http://stackoverflow.com/questions/10943137/difference-between-generic-type-and-wildcard-type) – Jayamohan 2013-02-28 01:08:22

+1

@Jayamohan我不同意。 – 2013-02-28 01:10:04

回答

3

Oracle

一個問題是出現的是:當我應該使用泛型方法,當我應該使用通配符類型?爲了理解答案,我們來看一下Collection庫中的幾個方法。

interface Collection<E> { 
    public boolean containsAll(Collection<?> c); 
    public boolean addAll(Collection<? extends E> c); 
} 

我們可以用通用的方法,而不是在這裏:

interface Collection<E> { 
    public <T> boolean containsAll(Collection<T> c); 
    public <T extends E> boolean addAll(Collection<T> c); 
    // Hey, type variables can have bounds too! 
} 

然而,在這兩個containsAll和中的addAll中,類型參數T只能使用一次。返回類型不依賴於類型參數,也沒有任何其他方法的參數(在這種情況下,只有一個參數)。這告訴我們,類型參數被用於多態;它的唯一影響是允許在不同的調用站點上使用各種實際參數類型。如果是這樣的話,應該使用通配符。通配符旨在支持靈活的子類型,這正是我們要在這裏表達的內容。

因此,第一個例子是因爲操作不依賴於類型。

第二,這是因爲它只取決於Number類。

+0

謝謝,所有的解釋都很好,但是這個由於某種原因幫助我最瞭解它。 – 2013-02-28 13:49:08

3

爲什麼使事情比他們需要更復雜?這些示例演示了the simplest thing that could possibly work - 這些示例並不試圖說明泛型方法。

爲什麼你會不會寫爲:

<T> void printCollection(Collection<T> c) { 
    for(T o : c) { 
     System.out.println(o); 
    } 
} 

因爲System.out.println()可以接受的對象,因此需要更具體的什麼都沒有。

這是爲什麼不寫成

public static <T extends Number> double sumOfList(List<T> list) { 
    double s = 0.0; 
    for (Number n : list) 
     s += n.doubleValue(); 
    return s; 
} 

再次,因爲你並不需要一個不同的參數爲每一個不同的T extends Number。接受List<? extends Number>的非通用方法就足夠了。

+0

我想我可能只是誤解了兩者之間的區別,還是他們是一樣的?用T而不是用T來解決我將會遇到的情況是什麼?我有一種感覺,我完全忽略了通配符的要點。 – 2013-02-28 01:14:08

+2

@JonTaylor:真的沒有任何情況下你不能用'T'而不是''來解決,但問題是'''不需要名字並且表示你真的不在乎類型是什麼。 – 2013-02-28 01:30:53

+0

@LouisWasserman'Class.forName'返回'Class '可能只是一個適用的通配符的例子。 – 2013-02-28 02:28:44

3

確實,如果方法參數類型具有帶有上限的第一級通配符,則它可以由類型參數替換。

計數器的例子(通配符不能由類型參數被替換)

List<?> foo() // wildcard in return type 

    void foo(List<List<?>> arg) // deeper level of wildcard 

    void foo(List<? super Number> arg) // wildcard with lower bound 

現在對於箱子可由任一通配符或類型參數

 void foo1(List<?> arg) 

    <T> void foo2(List<T> arg) 

人們普遍認爲,可以解決foo1()foo2()更時尚。這可能有點主觀。我個人認爲foo1()簽名更容易理解。而且業內也普遍採用foo1(),所以最好遵循這個慣例。

foo1()也對待arg更抽象一點,因爲你不能容易地在foo1()arg.add(something)。當然,這可以很容易解決(即通過arg到foo2()!)。公共方法看起來像foo1()也是一種常見做法,它在內部前往私人foo2()

還有一些情況下,通配符不會做,需要一個類型參數:

<T> void foo(List<T> foo1, List<T> foo2); // can't use 2 wildcards. 

這個討論至今大約是方法簽名。在其他地方,通配符在不能引入類型參數的情況下是不可或缺的。

+0

+1提到的類型參數[不能有下界](http://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword)。 – 2013-02-28 02:32:58