2017-02-22 150 views
2

全部, 要測試我在下面創建的示例代碼的Java泛型類型推斷。Java泛型類型推斷混淆

public static <U> U addBox2(U u1, U u2) { 
    return u2; 
} 

static interface A {} 
static interface B {} 
static class S {} 
static class C extends S implements A,B {} 
static class D extends S implements B,A {} 

如果你看到上面的A和S沒有任何關係。

在下面的代碼 -

A a = addBox2(new C(),new D()); 

我希望能收到編譯錯誤,因爲推斷出的類型是S和我將其分配給A和A和S有沒有關係還是這只是正常工作。

有人可以幫我解釋爲什麼這種行爲?

+2

A和S沒有關係,但既然你將C和D傳遞給方法,並實現A(和B)他們有一些共同點,即它們都是As。除此之外,您傳遞的所有內容都將是一個「對象」,並且這將是最不常見的分母。 – Thomas

+0

你爲什麼認爲它會輸入'S'?爲什麼你忽略實現的接口'A'和'B'? – Tom

+0

我使用IntelliJ IDEA進行開發,IDEA向我顯示T被推斷爲S.可能是IntelliJ向我展示了錯誤的東西。 – user3616964

回答

0

類型參數Ua變量的類型推斷出來,您將addBox2(...)的結果分配給您的情況,這在您的情況下爲A。因爲CD實施A它完美地工作。

當然,以下將不起作用:

S s = addBox2(new C(),new D()); 
A a = s; // compile error, type mismatch 
+0

我使用IntelliJ IDEA進行開發,IDEA顯示我T被推斷爲S.可能是IntelliJ向我展示了錯誤的東西。 – user3616964

1

按照要求:

AS沒有關係的確但因爲你傳遞一個CD的方法和兩實施A(和B)他們有一些共同點,即他們都是A s。

這意味着以下將工作:

A a = addBox2(new C(),new D()); //common type is A 
B b = addBox2(new C(),new D()); //common type is B 
S s = addBox2(new C(),new D()); //common type is S 
Object o = addBox2(new C(),new D()); //common type is Object 

只要類型推斷就可以解決從分配以及它應該工作參數的泛型類型(注意,類型推斷是不那麼好Java之前的版本,特別是5和6)。

你可以,但是,通過將其傳遞給方法調用自己定義的類型:

A a = EnclosingClass.<S>addBox2(new C(),new D()); //static method within EnclosingClass 
A a = this.<S>addBox2(new C(),new D()); //instance method 

在這兩種情況下,你定義泛型類型爲S,因此分配將無法正常工作。

0

我懷疑Eclipse和IntelliJ沒有正確註釋這些。有了這個(略有改動)例如:

public static <U> U getU(U u1, U u2) { 
    return u2; 
} 

static interface A {} 
static class S {} 
static class C extends S implements A {} 
static class D extends S implements A {} 
static class T implements A {} 

public static void main(String[] args) { 
    A a = getU(new C(), new D()); 
    A b = getU(new C(), new T()); 
} 

我看到下面的時候我鼠標懸停在main()第一次調用getU()

<? extends S> ? extends S analysis.Delme.getU(? extends S u1, ? extends S u2)

這顯然是不正確 - A不延伸S。我懷疑Eclipse在這裏錯誤地將泛型簡單化了,寧願報告CD都從S延伸,而不是兩者都實現A

如果我將鼠標懸停在第二個電話我,而不是得到合理得多:

<A> A analysis.Delme.getU(A u1, A u2)

這大概是編譯器是什麼實際上在這裏做了兩個電話。


注爲Thomas指出,即使兩個類沒有任何共同之處(如ST),他們仍然Object延長,所以Object c = getU(new S(), new T());是有效的。

0

不要相信編輯器的工具提示可能無法應對複雜的通用結構。推斷類型爲S & A & B(在Java 8之前),可以將其分配給A。在Java 8中,推斷類型僅爲A,因爲泛型方法調用是所謂的多表達式使用目標類型。對於您的addBox調用,它沒有區別。

爲了說明這個問題,你可以使用Java 7的兼容模式寫

Object o = Arrays.asList(new C(), new D()); 
在Eclipse

,並且將鼠標懸停在asList。這將打印
<? extends S> List<? extends S> java.util.Arrays.asList(? extends S... a),類似於addBox調用你的問題,但是當你的代碼更改爲

List<A> list = Arrays.asList(new C(), new D()); 

你的編譯器錯誤「類型不匹配:不能從List<S&A&B>轉換爲List<A>」(仍然在Java中7模式),顯示實際推斷的類型是S & A & B,而不是? extends S

當您將語言遵從性級別轉換爲Java 8時,編譯器錯誤將消失,如在Java 8中,目標類型將用於推斷類型,這將是A而不是S & A & B

至於說,你addBox調用,這都沒有區別AS & A & B是否被推斷,既可以分配給A,所以它的工作原理下兩者的Java 7和Java 8合規水平。但是當你使用一個調用,它在那裏有所作爲,就像Arrays.asList一樣,你可以看到實際推斷的類型,從不是? extends S作爲編輯器的工具提示聲明...