2011-10-30 15 views
4

考慮以下情形:參考方法是不明確的,與仿製藥,奇怪的行爲

class C { 
    void m(Class<?> c1, Class<?> c2) {} 
    <S, U extends S> void m(S s, U u) {} 
} 

class x {{ 
    final Class<Integer> cInteger = Integer.class; 
    final Class<?> cSomething = null; 
    final C c = new C(); 
    c.m(cInteger, cInteger); 
    c.m(cSomething, cSomething); // * 
}} 

使用Oracle的javac,版本1.7.0_01(也是1.7.0,還有的OpenJDK的Java 7和Java 6編譯器),我得到標有// *行錯誤:

error: reference to m is ambiguous, both method m(Class,Class) in C and method m(S,U) in C match

我不明白爲什麼會這樣:編譯器能夠判斷哪些方法被調用時,靜態類型的參數是Class<Integer>,但它與有問題。

IntelliJ的代碼分析說,這是好的,以及JRockit(或Sun的Java 6)編譯器。

所以,這裏顯然存在一個錯誤,無論是在那些說這是對的軟件中,還是在那些認爲是錯誤的軟件中。

需要注意的是,如果我刪除綁定的U(即,如果我聲明m<S, U> void M(S s, U u) {},它會通過編譯,而且,使用原始類型的調用(即Class x = null; m(x, x))編譯罰款以及。

因此,根據Java規範,這是代碼有效還是無效?

感謝。

回答

0

也許是因爲,如果你在方法調用說讓<S>Class<?>型,則<U>Class<?>和確定使用哪種方法是不可能的。

<S, U extends S> void m(S s, U u) {} //S=U=Class<?> => void m(Class<?> s, Class<?> u) {} //same signature 

如果你說讓<S><U>Class<Integer>類型,那麼它必須與仿製藥<S><U>第二種方法。

<S, U extend S> void m(S s, U u) {} //S=U=Class<Integer> => void m(Class<Integer> s, Class<Integer> u) {} //Ok 

問題

<S,U>聲明指定你的打算是用兩種不同的類型無關。

2

你確定嗎?我的測試是第一個m(cInteger, cInteger)失敗,第二個m(cSomething, cSomething)是好的。原始的m(x,x)也不能編譯。

(1)m(cInteger,cInteger)失敗

兩種米()相匹配的參數;對於平方米,推斷產量S=U=Class<Integer>

現在的問題是,m1更具體比m2?按照15.12.2.5的程序,是。如果是這樣的話,就沒有含糊之處; m1應該被選爲最具體的方法,並且調用應該被編譯。

然而,這違反了由規範給出的非正式要求:如果M1平方米,通過M1可以轉嫁處理的任何調用更具體的平方米沒有編譯時鍵入錯誤。例如,m1可以接受參數(Class<Integer>,Class<String>),其中平方米不能(由於不完美類型推斷 過程)。

Javac顯然堅持非正式的概念;這可能是因爲正式規範有一個錯誤 - 它應該包括捕獲轉換(稍後解釋)在更具體的關係的定義中;那麼m1並不比m2更具體,因此呼叫m(cInteger,cInteger)是不明確的。

如果另一個編譯器嚴格遵守形式規範,繼承規範的錯誤不是它的錯。

(2)m(x, x)失敗

出於同樣的原因:(1);兩種方法都是匹配的,但都不是比另一種更具體。

M1通過方法調用轉換,它允許未選中轉換從原料ClassClass<?>匹配。推斷後平方米匹配S=U=Class

(3)m(cSomething, cSomething)編譯

這是因爲M1是唯一適用一個,因而不存在模糊。

m2不適用,讓我們來看看爲什麼。

首先,參數的類型不完全是(Class<?>,Class<?>) - 捕獲轉換先應用於它們。 (同樣,規範很不明確(見第15章),但我很確定這是很好理解的情況;任何表達式的類型應用捕獲轉換)

所以參數類型是(Class<X1>,Class<X2>),有兩個新鮮的類型變量。這裏還有另外一個問題,一個更精確的轉換可能是(Class<X1>,Class<X1>),不幸的是,捕捉轉換應用了兩次,無論是兩種不同的類型。

m1很容易與參數類型匹配。但平方米不匹配,由於不完美的類型推斷過程。該過程首先產生S=Class<X1>, U=Class<X2>,之後,類型變量的邊界被檢查,其中U extends S失敗。

(4)移除將U結合

現在(1),(2),(3)所有編譯。因爲沒有U extends S,推斷通過。

對於(1)和(2)中,M1 現在比平方米,不再模糊更加具體。

對於(3),m2現在匹配;但隨後被更具體的m1