2009-08-18 86 views
16

Java通常可以基於參數(甚至在返回類型上,與例如C#相比)推斷泛型。返回類型推斷的通配符泛型

個例子:我有一個泛型類Pair<T1, T2>這只是存儲的一對值,可以通過以下方式使用:

Pair<String, String> pair = Pair.of("Hello", "World"); 

的方法of看起來就像這樣:

public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
    return new Pair<T1, T2>(first, second); 
} 

非常好。但是,這已不再適用於以下用例,這需要通配符:(注意顯式類型轉換,使List.class正確的類型)

Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello"); 

代碼失敗,出現以下錯誤(提供通過Eclipse中):

類型不匹配:不能從TestClass.Pair<Class<capture#1-of ?>,String>轉換爲TestClass.Pair<Class<?>,String>

然而,顯式調用構造函數仍然按預期工作:

Pair<Class<?>, String> pair = 
    new Pair<Class<?>, String>((Class<?>) List.class, "hello"); 

有人能解釋這種現象?它是否由設計?是想要?我做錯了什麼,或者我偶然發現了編譯器中的設計/錯誤?

胡亂猜測:「捕獲#1-的?」不知何故似乎暗示通配符由上飛編譯器填充,使得類型Class<List>,因而失敗的轉換(從Pair<Class<?>, String>Pair<Class<List>, String>) 。這是正確的嗎?有沒有辦法解決這個問題?


爲了完整起見,這裏是Pair類的簡化版本:

public final class Pair<T1, T2> { 
    public final T1 first; 
    public final T2 second; 

    public Pair(T1 first, T2 second) { 
     this.first = first; 
     this.second = second; 
    } 

    public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
     return new Pair<T1, T2>(first, second); 
    } 
} 
+0

看起來轉換器看到「of」的簽名,因爲它返回一個Pair <?擴展類,?擴展類>類型。 對於最終的類,它似乎足夠聰明,減少了擴展部分,這就是爲什麼它不會在字符串上投訴。 – Zed 2009-08-18 15:08:43

+0

嗯,有趣。感謝您在此鏈接我。 – jjnguy 2011-06-13 19:14:30

+0

它現在在java8中工作。目標類型也被諮詢爲inferene。 – ZhongYu 2015-08-06 17:25:21

回答

13

構造成功運作的原因是,你明確指定類型參數。

Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello"); 

當然,整個的原因,你必須擺在首位的靜態方法可能只是爲了讓類型推斷(不帶處工作的構造函數:靜態方法,如果你這樣做也將工作所有)。

這裏的問題(如你所建議的)是編譯器執行capture conversion。相信這是由於 [§15.12.2.6 of the JLS]結果:

  • 所選方法的結果類型被確定如下:
    • 如果被調用的方法被聲明用的空隙, 返回類型那麼結果是無效的。
    • 否則,如果未選中轉換對於 方法適用是必要的,那麼 結果類型是該方法的聲明返回類型的刪除(第4.6節) 。
    • 否則,如果被調用的方法是通用的,那麼對於1英寸,讓 網絡連接是 形式類型參數的方法中,讓艾是實際的類型推斷方法 調用 參數,令R爲所述聲明 返回類型的方法是 調用。通過將捕獲轉換 (§5.1.10)應用於R [F1:= A1,...,Fn:= An],獲得結果類型 。
    • 否則,通過在方法聲明中將捕獲 轉換(第5.1.10節)應用於 類型獲得結果類型。

如果你真的想要的推論,一個可能的解決辦法是做這樣的事情:

Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello"); 

變量pair將有更廣闊的類型,它也意味着位更多地輸入變量的類型名稱,但至少不需要再投入方法調用。

+0

非常感謝。我仍在考慮解決方案是否使核心更加混亂。目前,我會保持原樣。 – 2009-08-18 15:22:15