2013-02-01 74 views
11

在下面的示例中,爲什麼編譯器能夠推斷Foo.test()中第一次調用Foo.create()的通用參數,但第二次不能這樣做?我使用Java 6爲什麼javac不能推斷用作參數的函數的泛型類型參數?

public class Nonsense { 
    public static class Bar { 
     private static void func(Foo<String> arg) { } 
    } 

    public static class Foo<T> { 

     public static <T> Foo<T> create() { 
      return new Foo<T>(); 
     } 

     private static void test() { 
      Foo<String> foo2 = Foo.create(); // compiles 
      Bar.func(Foo.create());   // won't compile 
      Bar.func(Foo.<String>create()); // fixes the prev line 
     } 
    } 
} 

(編譯錯誤是在類型Nonsense.Bar的方法FUNC(Nonsense.Foo)不適用於參數(Nonsense.Foo))。

注意:我知道編譯器錯誤可以通過test()中的第三行修復 - 我很好奇是否存在一個特定的限制,防止編譯器能夠推斷出類型。它出現對我來說,這裏有足夠的上下文。

+4

我不確定你期望得到什麼答案,除了「它不夠聰明」。 –

+0

@路易斯 - 可以想象,這是不可能夠聰明,但我還沒有弄清楚爲什麼。 – bacar

+0

@bacar:它可能足夠聰明,但它不是。 –

回答

14

從Java 7中,方法重載具有您所呼叫的方法的任何目標類型信息之前進行可以考慮試圖推斷在func的聲明中的類型變量T。這看起來很愚蠢,因爲我們都可以看到,在這種情況下,只有一個方法名爲func,但是,它由JLS強制執行,並且是來自Java 7的javac的行爲。

編譯過程如下所示:首先,編譯器發現它正在編譯對名爲func的Bar類的靜態方法的調用。要執行重載解析,它必須找出該方法被調用的參數。儘管這是一個微不足道的情況,它仍然必須這樣做,直到它完成後,它纔沒有任何有關可用於幫助它的方法的正式參數的信息。實際參數由一個參數組成,對Foo.create()的調用被聲明爲返回Foo<T>。同樣,沒有目標方法的標準,它只能推斷返回類型是Foo<T>的刪除,它是Foo<Object>,並且它這樣做。

方法過載分辨率然後失敗,因爲func的過載沒有一個與Foo<Object>的實際參數兼容,並且發生此錯誤。

這當然是非常不幸的,因爲我們都可以看到,如果信息可以簡單地在另一方向流動,從方法調用返回到調用位置的目標,可以很容易推斷出該類型,沒有錯誤。事實上,Java 8中的編譯器可以做到這一點,而且確實如此。如另一個回答所述,這種更豐富的類型推斷對於在Java 8中添加的lambda表達式以及正在利用lambda表達式的Java API的擴展非常有用。

您可以從上述鏈接下載預發佈版本Java 8 with JSR 335 lambdas。它編譯問題中的代碼時沒有任何警告或錯誤。

3

從上下文中推斷類型太複雜了。主要障礙可能是方法重載。例如,f(g(x)),要確定應用哪個f(),我們需要知道g(x)的類型;但g(x)的類型可能需要從f()的參數類型推斷。在某些語言中,方法重載是被禁止的,因此類型推斷可以更容易。

在Java 8中編譯您的示例。由於lambda表達式用例,Java團隊更願意擴大類型推斷。這不是一件容易的事。

對Java 7 Java語言規範包含40頁正好符合規範的方法調用表達式(第15.12)

相關問題