2013-10-18 83 views
7

我有以下類別:廠和泛型

public interface IDataSource<T> { 
    public List<T> getData(int numberOfEntries); 
} 

public class MyDataSource implements IDataSource<MyData> { 
    public List<MyData> getData(int numberOfEntries) { 
    ... 
    } 
} 

public class MyOtherDataSource implements IDataSource<MyOtherData> { 
    public List<MyOtherData> getData(int numberOfEntries) { 
    ... 
    } 
} 

我想用一個工廠,返回基於數據類型的正確實施。我寫了以下,但我得到「未經檢查的投」警告:

public static <T> IDataSource<T> getDataSource(Class<T> dataType) { 
    if (dataType.equals(MyData.class)) { 
     return (IDataSource<T>) new MyDataSource(); 
    } else if (dataType.equals(MyOtherData.class)) { 
     return (IDataSource<T>) new MyOtherDataSource(); 
    } 

    return null; 
} 

我做錯了嗎?我能做些什麼來擺脫警告?

+0

這並不回答你的問題,但你可以安全地使用'=='來代替'equals'的'Class'es。 – Boann

回答

3

泛型用於編譯時類型安全。它們不能用於像那樣的運行時類型確定。要擺脫警告,您可以執行類似@SuppressWarnings("unchecked")或使用-Xlint:-unchecked編譯器標誌,如"Raw Types" part of the Java tutorial中所述。

5

我不知道有任何方法擺脫這些警告沒有@SuppressWarnings("unchecked")

您正在傳遞一個Class對象,因此可以捕獲到T。但是你不得不在運行時檢查Class以確定哪個IDataSource<T>返回。此時,類型擦除早已發生。

在編譯時,Java不能確定類型的安全性。它不能保證運行時Class中的T與返回的IDataSource<T>中的T相同,因此它會產生警告。

這看起來像是當你被迫使用@SuppressWarnings("unchecked")註解該方法來刪除警告的時候。這種警告是有原因的,所以您要提供並確保類型安全。正如所寫,看起來你已經提供了類型安全。

@SuppressWarnings("unchecked") 
public static <T> IDataSource<T> getDataSource(Class<T> dataType) { 
4

你做得對,你應該簡單地壓制警告。工廠是仿製藥領域中棘手的領域之一,您需要手動將其轉換爲泛型類型,並且必須通過任何方式確保返回值與您傳入的Class<T>匹配。例如,在這種情況下,您硬編碼一些IDataSource實現,所以我會建議編寫單元測試來驗證類型是否正確,以便如果MyData實現以不兼容的方式更改,則會在構建時發生錯誤。

只需註釋getDataSource方法@SuppressWarnings("unchecked"),並且在抑制警告時添加解釋性註釋總是一個好主意。

3

其他答案已經回答了你提出的問題。但是我想退一步瞭解一下你用這種工廠方法試圖完成的事情。該工廠基本上提供了一個到IDataSource參數的數據類型圖。 Dependency injection可能是更合適的模式,因爲這是一個衆所周知的小數據類型和實現(如您的示例所示)。

比方說,你要存儲在蒙戈在MySQL中的所有Widgets但所有Gadgets,你可能有兩類:實現IDataSource<Widget>一個MongoWidgetDataSource和實現IDataSource<Gadget>一個MysqlGadgetDataSource

而不是在數據使用者內硬編碼工廠方法調用如MyFactory.getDataSource(Widget.class),我會注入適當的IDataSource依賴項。我們可能有MyService,它使用小部件(存儲在mongo中)。使用你提出了一個工廠應該是這樣的:

public class MyService { 
    public void doSomething() { 
    String value = MyFactory.getDataSource(Widget.class).getSomething(); 
    // do something with data returned from the source 
    } 
} 

相反,你應該注入適當的數據源作爲構造ARG到服務:

public class MyService { 
    private final IDataSource<Widget> widgetDataSource; 

    public MyService(IDataSource<Widget> widgetDataSource) { 
    this.widgetDataSource = widgetDataSource; 
    } 

    public void doSomething() { 
    String value = widgetDataSource.getSomething(); 
    // now do something with data returned from the source 
    } 
} 

這具有使額外的好處你代碼更可重用,更容易進行單元測試(模擬依賴)。

然後,在您實例化MyService時,您還可以連接數據源。許多項目使用依賴注入框架(如Guice)使這更容易,但它不是一個嚴格的要求。但個人而言,我從來沒有在沒有任何實際規模或持續時間的項目上工作。

如果不使用DI框架,你只需實例化的依賴關係,當你創建調用服務:

public static void main(String[] args) { 
    IDataSource<Widget> widgetDataSource = new MongoWidgetDataSource(); 
    IDataSource<Gadget> gadgetDataSource = new MysqlGadgetDataSource(); 
    MyService service = new MyService(widgetDataSource, gadgetDataSource); 
    service.doSomething(); 
} 

在吉斯,你會線了這些數據的來源是這樣的:

public class DataSourceModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    bind(new TypeLiteral<IDataSource<Widget>>() {}).to(MongoWidgetDataSource.class); 
    bind(new TypeLiteral<IDataSource<Gadget>>() {}).to(MysqlGadgetDataSource.class); 
    } 
} 

依賴倒置對思考問題有點不同,但它可能導致更多的解耦,可重用和可測試的代碼庫。

1

這似乎工作:

public static <T> IDataSource<T> getDataSource(MyData dataType) { 
    System.out.println("Make MyDataSource"); 
    return (IDataSource<T>) new MyDataSource(); 
} 

public static <T> IDataSource<T> getDataSource(MyOtherData dataType) { 
    System.out.println("Make MyOtherDataSource"); 
    return (IDataSource<T>) new MyOtherDataSource(); 
} 

public void test() { 
    IDataSource<MyData> myDataSource = getDataSource((MyData) null); 
    IDataSource<MyOtherData> myOtherDataSource = getDataSource((MyOtherData) null); 
} 

您可能希望創建空的原型,而不是投null像我有,但我認爲這是一個可行的技術。