2011-02-07 65 views
2

我想要使用接口創建一個引用某種服務的類。該服務可以有不同的實現。不同的實現將處理不同類型的請求。在過去,我會定義一個接口是這樣的:使用泛型的可重用接口

public interface I_Test 
{ 
    public String get(String key, Enum type); 
} 

,並實現它是這樣的:

public class Test_1 implements I_Test 
{ 
    public String get(String key, Enum type) 
    { 
     Enum_1 t1 = (Enum_1)type; 

     switch(t1) 
     { 
      case NAME: 
       return "Garry"; 
      case DOB: 
       return "1966"; 
      default: 
       throw new IllegalArgumentException("Unkown type [" + type + "]"); 
     } 
    } 
} 

好是我可以用不同的實現我的接口,以滿足不同的需求。 不好的是我必須鍵入強制轉換,因此在運行時有風險。

我希望仿製藥可以解決這個問題,所以我這樣做:

public interface I_Test<T extends Enum> 
{ 
    public String get(String key, T type); 
} 

這:

public class Test_1 implements I_Test<Enum_1> 
{ 
    public String get(String key, Enum_1 type) 
    { 
     switch(type) 
     { 
      case NAME: 
       return "Garry"; 
      case DOB: 
       return "1966"; 
      default: 
       throw new IllegalArgumentException("Unkown type [" + type + "]"); 
     } 
    } 
} 

,但是當我去使用我得到的類型安全警告,除非我的東西聲明我的變量與我打算使用的類型,如下所示:

I_Test<Enum_1> t1 = new Test_1(); 

這真的讓我感到困擾,因爲th創建I_Test接口的整個目的是讓我可以使用不同的實現,但似乎我必須在編譯時鎖定到特定類型以避免此警告!

有沒有什麼辦法可以編寫一個可重用的界面,使用泛型沒有這個惱人的警告?

+0

我真的不明白你的界面做了什麼(這是什麼意思「以處理不同類型的請求」,在什麼情況下?),但它看起來像一個接口的濫用 - 主要是因爲你的實現使用`switch`來代替多態性。看起來你的上述邏輯可以通過`Map ` – davin 2011-02-07 18:59:29

+0

更容易完成,我必須刪除敏感數據,應該做得更好 - 對於混淆抱歉。我可以說的是get獲取數據和請求類型。請求的類型取決於實現 - 當前有4個支持的實現以及30多種不同的請求類型,分散在我工作的地方。 – BigMac66 2011-02-07 19:52:45

回答

4

泛型的要點是確保您的代碼更可靠(就類型安全而言)。通過泛型,您可以在編譯時而不是運行時找到類型不兼容的問題。當你將接口定義爲I_Test<T extends Enum>時,基本上是說你需要根據特定的類型將接口進行通用化。這就是爲什麼Java給你一個警告。

如果你做了這樣的Map myMap = new HashMap<string>();,你會得到同樣的警告。

在Java中,您實際上指定了類型,並且它們不是從RHS上的內容中推斷出來的(除非您執行類似Integer i = 1的操作,但這是自動裝箱)。由於您對接口進行了通用化,因此當您使用該接口聲明某些內容時,需要指定要使用的類型(泛型)。

當你實例化一個泛型類型時,編譯器將通過使用稱爲「type erasure」的東西來翻譯這些類型。在這裏,編譯器刪除所有與類型參數和類型參數相關的信息。 Java這樣做是爲了保持與在Java有泛型之前編寫的舊代碼的兼容性。

因此I_Test<Enum_1>實際上在編譯期間被轉換爲原始類型I_Test。使用原始類型通常被認爲是不好的做法(因此,這種「令人討厭的警告」)。編譯器告訴你它沒有足夠的信息來執行類型檢查,因此它不能確保類型安全(因爲你使用了一個原始類型)。

要了解更多關於仿製藥,看看下面的例子:

1

泛型約編譯時警告。如果你不想要它們,不要使用它們。

說了這麼多,你可以創建不同的,非通用子接口,例如:

public interface Enum_1_Test extends I_Test<Enum_1> { 
    ... 
} 

然後聲明類爲

public class Test_1 implements Enum_1_Test 

但我不相信這是非常有用。作爲一個經驗法則,如果你有一個適用於許多輸入類型的實現,並且如果你想爲每個輸入類型單獨實現,就使用舊的多態,那麼你想要使用泛型。

1

原始I_Test支持任何枚舉類型作爲參數,而Test_1實現僅支持有限子集(Enum_1),這是因爲Test_1被指定爲僅對一個枚舉類型實現I_Test。

這是一個爲什麼編譯器發出警告的示例,下面的代碼編譯自I_Test的原始類型接受任何枚舉,但由於Test_1僅支持Enum_1,它將拋出類轉換異常。

enum MyEnum{A} 
I_Test t1 = new Test_1();//warning here 
t1.get("",MyEnum.A);//Exception at runtime, but compiles fine 

如果指定泛型類型,將導致編譯錯誤,這比運行時異常更受歡迎。

enum MyEnum{A} 
I_Test<Enum_1> t1 = new Test_1(); 
t1.get("",MyEnum.A);//Does not compile