2014-01-15 39 views
14

我有一個奇怪的Java泛型歧義的行爲我無法解釋:泛型歧義與& - 運算符和秩序

這3種方法類:

public static <E extends ClassA & ClassB> void method(E val) {} 
public static <E extends ClassC & ClassB & ClassA> void method(E val) {} 
public static <E extends ClassB> void method(E val) {} 

編譯罰款。

但那些沒有(歧義衝突):

public static <E extends ClassA & ClassB> void method(E val) {} 
public static <E extends ClassB & ClassC & ClassA> void method(E val) {} 
public static <E extends ClassB> void method(E val) {} 

(!ClassA的,ClassB的,ClassC都是完全獨立的接口)

+0

通過'Class',我認爲你的意思是界面? – SLaks

+0

+1我今天學到了一些新東西。 – SLaks

回答

12

由於類型擦除,編譯器需要選擇一個靜態已編譯方法中參數類型的已知類型。

爲此,它使用約束列表中的第一個類型。

在你的第一個例子中,這導致了一種獨特類型爲每個方法,因此它編譯成

public static method(ClassA val); 
public static method(ClassC val); 
public static method(ClassB val); 

這是完全合法的(除了丟失的返回類型);它使用三種不同的參數類型創建三個重載。

在第二個例子中,這將導致不確定性:

public static method(ClassA val); 
public static method(ClassB val); 
public static method(ClassB val); 

這是不合法的,因爲後兩種方法具有相同的簽名。

該規範explicitly documents此行爲。

這可能是合法的,試圖從每個重載中選擇一個約束類型,使得沒有衝突,但是對於較大的約束列表而言,這會變得很複雜&。
該規範可能會說是這樣的:

如果在在參數列表中的類型的擦除所使用的,在一般方法的類型可變的擦除被選擇爲使得該方法的結果的每一個過載在擦除後的一個獨特的簽名。
如果沒有擦除組合會導致唯一簽名,則會發生歧義錯誤。

我懷疑這個問題是在NP中。