2012-09-10 45 views
4

假設我有像這樣Java泛型類型中隱藏的參數

public interface Foo<Ret, Arg> { 
    public Ret doSomething(Arg arg); 
} 

而具體的實現類,只有具有包可見的接口。

class FooImpl<Ret, Arg1, Arg2> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Arg2> { 
    // This class also implements something else making use of Arg2, so you cannot safely remove it 
    // But nothing relating to Arg2 is needed to create a FooImpl 
} 

(這如何可能發生的一個例子是Foo由兩個接口聲明爲一個可變參數方法FooImpl轉發方法的實現和Bar

現在,假設有一個靜態方法在某處作爲Foo返回FooImpl的相同包。人們會認爲你可以使用return new FooImpl<Ret, Arg1, ?>(...),但實際上你不能(你必須使用具體的虛擬類型,如Arg2,即...... return new FooImpl<Ret, Arg1, Object>(...))。

任何想法,爲什麼這是,尤其是因爲Foo接口和軟件包的可見性有效地隱藏FooImpl從任何使用靜態方法?是否是因爲人們仍然可以在某種程度上使用反射來達到需要具體類型的FooImplBar部分?

+1

你得到的錯誤信息是什麼?你是爲了構造函數還是在試圖將其轉換回來時獲得它?那個靜態方法的返回簽名是什麼? – Thilo

回答

1

Java編譯器儘量不要太聰明。在很多情況下,顯然它不需要一些信息來生成正確的代碼,但它仍然抱怨。當Java被髮明時,世界已經看到了C,儘管它試圖編譯所有東西,但不管多麼危險或愚蠢,它都是馬虎的態度。 javac的目標是確保人們不能shoot themselves into the foot easily

因此,它假設有一個原因,你提到Arg2 - 如果沒有必要的東西,你一定不會把它放到代碼中。

解決方案:

  1. 當你實現Bar分配類型:class FooImpl<Ret, Arg1> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Object>
  2. 創建BetterFooImpl<Ret, Arg1> extends FooImpl<Ret, Arg1, Object>。這隱藏了API的消費者的第三種觀點。
  3. 試着擺脫第三個參數Bar
  4. 小心使用泛型。這可能會令人驚訝,但有些情況下泛型不能很好地工作。這通常是由於細微的實現細節造成了@SuppressWarning註釋的爆炸。當發生這種情況時,我會問這個問題,當我找不到我能理解的解決方案時,我會刪除這個泛型。編寫可維護的代碼比通過泛型迷宮尋找聰明的方法更重要。
+0

+1它回答了真正的問題 –

4

我認爲這與您的特定設計無關。

你只是不能用通配符實例化類型。

這是行不通的,無論是:

return new ArrayList<?>(); 

如果它沒有,它會做任何事情比

return new ArrayList<Object>(); 

你需要一個具體的類,但這並不意味着你需要在您的工廠方法簽名中顯示它:

static <A,B> Foo<A,B> makeFoo(){ 
    return new FooImpl<A,B, SomeTypeThatNoOneNeedsToSee>(); 
}