2014-07-21 23 views
6

我試圖將項目切換到Java8,並遇到Eclipse Luna和javac類型推斷之間的奇怪差異。使用JDK 1.7.0_65 javac,這段代碼編譯得很好。 JDK 1.8.0_11抱怨toString(char [])和toString(Throwable)匹配「toString(getKey(code,null));」線。 Eclipse的月神4.4(I20140606-1215)與任一JDK愉快地進行編譯:類型推斷中的差異JDK8 javac/Eclipse Luna?

public class TypeInferenceTest { 
    public static String toString(Object obj) { 
     return ""; 
    } 

    public static String toString(char[] ca) { 
     return ""; 
    } 

    public static String toString(Throwable t) { 
     return ""; 
    } 

    public static <U> U getKey(Object code, U defaultValue) { 
     return defaultValue; 
    } 

    public static void test() { 
     Object code = "test"; 
     toString(getKey(code, null)); 
    } 
} 

我認爲那些可能匹配的toString(對象)的唯一簽名。

當然,我可以簡單地添加強制轉換爲對象,但我不知道爲什麼javac的不能infere本身的類型(而日食一樣),爲什麼赫克的javac考慮的Throwable和的char []合適的配對,但不是對象。

這是Eclipse或javac中的錯誤嗎? (我的意思是隻有一個編譯器就在這裏,它要麼編譯或事實並非如此)

編輯:從javac的(JDK8)錯誤消息:

C:\XXXX\Workspace\XXXX\src>javac -cp . TypeInferenceTest.java 
TypeInferenceTest.java:22: error: reference to toString is ambiguous 
       toString(getKey(code, null)); 
       ^
    both method toString(char[]) in TypeInferenceTest and method toString(Throwable) in TypeInferenceTest match 
1 error 
+0

哎呀,它說的盧娜在約。 – Durandal

回答

3

編譯器只能檢查方法簽名,不方法體,這部分是無關緊要的。

這種 「減少」 的代碼(僞代碼):

public class TypeInferenceTest { 
    public static String toString(Object obj); 

    public static String toString(char[] ca); 

    public static String toString(Throwable t); 

    public static <U> U getKey(Object code, U defaultValue); 

    public static void test() { 
     Object code = "test"; 
     toString(getKey(code, null)); 
    } 
} 

還要注意的是<U> U getKey(...)真的是:<U extends Object> U getKey(...)

所有它知道getKey(code, null)返回是:? extends Object,所以它返回一個子類型Object,或Object本身。
沒有符合三個特徵,即Objectchar[]Throwable,其中兩個char[]Throwable比賽一視同仁,優於Object,因爲你問一個? extends Object

所以它不能選擇哪個是正確的,因爲所有三個匹配簽名。

當你將它更改爲:

public static Object getKey(Object code, Object defaultValue); 

那麼只有public static String toString(Object obj);比賽,因爲它更好任何其他? extends Object這不等於Object匹配。

編輯,我看了一下這個問題的原意:爲什麼它在Java 7中編譯,而不是在Java 8中編譯?

在Java 8類型推理中得到了很大的改進。

鑑於在Java 7中它只能推斷getKey返回Object,它現在在Java 8中推斷它返回? extends Object

使用Java 7時,只有一個匹配,即Object

有變動,甚至更好的可視化,考慮這段代碼:

public class TypeInferenceTest { 
    public static String toString(Object obj) { return "1"; } 

    public static String toString(Throwable t) { return "2"; } 

    public static <U> U getKey(Object code, U defaultValue) { return defaultValue; } 

    public static void test() { 
     Object code = "test"; 
     String result = toString(getKey(code, null)); 
     System.out.println(result); 
    } 

    public static void main(String[] args) { 
     test(); 
    } 
} 

在Java 7中它打印1,關於Java 8它打印2,正是因爲我有上述原因。

+1

我只提供方法體,因爲它使代碼*可編譯*。當然,這些並不是真正的東西(getKey不是靜態的,也不屬於同一個類),它們也不相關。他們在那裏使推理問題成爲* only *錯誤。你打破了你對類型推斷過程的解釋,但是這並不能解釋爲什麼它會在eclipse或javac 7中編譯,除非它編譯*是* a(仍然存在於java7中)錯誤。 – Durandal

+0

@Durandal類型推斷在Java 8中得到了很大改進。 – skiwi

+0

我不同意你的結論,認爲這是對類型推斷的改進。我會認爲它是一個錯誤。 – rolfl

1

javac實際上可能是正確的。規範writes

空類型有一個值,空引用,由空字符空值表示,由ASCII字符形成。

因此,null的類型是空類型。

表達式getKey(code, null)是泛型方法的方法調用表達式。 defines其類型如下規範:

  • 如果所選擇的方法是通用的,方法調用不提供顯式類型參數,如在§18.5.2指定的調用類型推斷。

類型推理算法的實際描述是相當複雜的,但推斷U類型必須是空類型分配。唉,對於所有參考類型都是如此,那麼選擇哪一個?最合乎邏輯的是最具體的這種類型,即空類型。因此,方法調用表達式的類型可能是空類型。

現在,方法調用表達式toString(getKey(code, null))引用哪個方法?規格writes

第二步搜索成員方法在上一步中確定的類型。此步驟使用方法的名稱和參數表達式來定位既可訪問又適用的方法,即可以在給定參數上正確調用的聲明。

可能有多個這樣的方法,在這種情況下,選擇最具體的方法。最具體方法的描述符(簽名加返回類型)是在運行時用來執行方法分派的方法。

由於參數類型爲空類型,因此所有三種toString方法都適用。該規範寫道:

的方法被認爲是最大限度地爲特定的方法調用,如果它是方便和適用,沒有其他是適用的,可訪問是嚴格更具體的方法。

如果只有一個最具體的方法,那麼該方法實際上是最具體的方法;它必然比任何其他適用的可訪問方法更具體。然後按照§15.12.3的規定進行進一步的編譯時檢查。

有可能沒有最具體的方法,因爲有兩種或更多方法是最具體的。在這種情況下:

  • 如果所有的最大具體方法有覆蓋等效簽名(§8.4.2),則:

    • 如果的最大具體方法只有一個是具體的(即是非抽象的或默認的),它是最具體的方法。否則,如果所有最大特定方法都是抽象或默認的,並且所有最大特定方法的簽名具有相同的擦除(§4.6),那麼最具體的方法是在子集中任意選擇的最具體的方法具有最具體的返回類型。

      在這種情況下,最具體的方法被認爲是抽象的。此外,當且僅當在每個最大特定方法的throws子句中聲明瞭該異常或其擦除時,纔會考慮最具體的方法拋出checked異常。

  • 否則,方法調用是不明確的,並且會發生編譯時錯誤。

兩個toString(char[])toString(Throwable)更具體的說toString(Object),但也不是比其他更具體的,也不是他們的簽名覆蓋當量。

因此,方法調用是不明確的,並被編譯器拒絕。