2013-10-18 28 views
7

は、私は以下のクラスを持っています。私は、次のことを書いたが、私は「未チェックのキャスト」の警告が出ます:工場とジェネリック

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コンパイラフラグを使用します。

5

私は@SuppressWarnings("unchecked")なしでこれらの警告を取り除く方法について知らない。

Classオブジェクトを渡しているので、Tをキャプチャできます。しかし、実行時にClassを確認して、返すものを確認することが強制されます。この時点で、タイプ消去は発生してから長いです。

コンパイル時に、Javaは型の安全性を確認できません。実行時にClassTIDataSource<T>のと同じであることを保証することはできませんので、警告が生成されます。

これは、警告を削除するために@SuppressWarnings("unchecked")というメソッドに注釈を付ける必要があるときのようです。その警告は理由があるため、タイプの安全を提供し保証するのはあなた次第です。書かれているように、型の安全性を提供しているようです。

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

あなたは正しいことをしており、単に警告を抑制するだけです。ファクトリはジェネリックの複雑な領域の1つで、ジェネリック型に手動でキャストする必要があり、戻り値がClass<T>と一致することを保証する必要があります。たとえば、この場合はコンパイルをIDataSourceハードコーディングしているので、互換性のない方法でMyDataの実装が変更された場合、ビルド時にエラーが発生するように、型が正しいことを確認する単体テストを作成することをお勧めします。

getDataSourceメソッドには@SuppressWarnings("unchecked")と注釈を付けるだけです。警告を抑制するときは、説明的なコメントを追加することをお勧めします。

3

他の回答が、あなたがそれを引き起こしたときに問題に回答しました。しかし、私はあなたがこのファクトリメソッドで達成しようとしていることを理解するために一歩前進したいと思います。このファクトリは基本的にデータ型のマップをIDataSourceのパラメータに提供します。 Dependency injectionは、これがデータ型と実装の小さな既知のセットであるため(例のように)、より適切なパターンになる可能性があります。 IDataSource<Widget>IDataSource<Gadget>を実装MysqlGadgetDataSourceを実装していMongoWidgetDataSource

あなたはモンゴ内のすべてのWidgetsが、MySQLのすべてのGadgetsを格納したいとしましょう、あなたは、2つのクラスを持っているかもしれません。

データコンシューマ内に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 
    } 
} 

代わりに、あなたがサービスにコンストラクタ引数として適切なデータソースを挿入する必要があります

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など)を使用していますが、厳密な要件ではありません。個人的には、実際のサイズや期間を持たないプロジェクトでは決して作業しません。

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

Guiceのでは、あなたはこのように、これらのデータソースを配線します:あなたはDIフレームワークを使用しない場合は、呼び出し元のサービスを作成するときに

は、あなただけの依存関係をインスタンス化

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); 
    } 
} 

依存関係の逆転は、問題について考えるのとは少し違う方法ですが、デカップルされた、再利用可能でテスト可能なコードベースにつながります。

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を作成することを好むかもしれませんが、私は、これは実行可能な技術であると思う: