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) {
    ...
  }
}

データ型に基づいて正しい実装を返すファクトリを使用したいと考えています。次のように書きましたが、「Unchecked cast」という警告が表示されます。

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

私はそれを間違っていますか?警告を取り除くにはどうすればよいですか?

4

5 に答える 5

5

なしでこれらの警告を取り除く方法を知りません@SuppressWarnings("unchecked")

Classオブジェクトを渡しているのでT、キャプチャできます。Classただし、実行時にをチェックして、どちらIDataSource<T>を返すかを決定する必要があります。現時点では、型消去はかなり前に発生しています。

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

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

@SuppressWarnings("unchecked")
public static <T> IDataSource<T> getDataSource(Class<T> dataType) {
于 2013-10-18T23:16:19.620 に答える
4

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

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

于 2013-10-18T23:18:16.413 に答える
3

ジェネリックは、コンパイル時のタイプ セーフのためのものです。そのような実行時の型決定には使用できません。警告を取り除くには、Java チュートリアル の「生の型」の部分で説明されているように、次のようなことを行うか、コンパイラ フラグを@SuppressWarnings("unchecked")使用します。-Xlint:-unchecked

于 2013-10-18T23:16:05.887 に答える
3

あなたが提起したように、他の答えは問題に答えました。しかし、このファクトリ メソッドで何を達成しようとしているのかを理解するために、一歩下がってみたいと思います。このファクトリは基本的に、データ型からIDataSourceパラメータへのマップを提供します。これはよく知られているデータ型と実装の小さなセットであるため (例で示されているように)、依存性注入がより適切なパターンになる可能性があります。

Widgetsすべてを Mongo に保存し、すべてGadgetsを Mysqlに保存したいとしMongoWidgetDataSourceます。IDataSource<Widget>MysqlGadgetDataSourceIDataSource<Gadget>

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

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

Guice では、これらのデータ ソースを次のように接続します。

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

依存関係の逆転は、この問題についての考え方とは少し異なりますが、より分離された、再利用可能でテスト可能なコード ベースにつながる可能性があります。

于 2013-10-19T00:50:57.393 に答える
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が、これは実行可能な手法だと思います。

于 2013-10-19T16:41:20.347 に答える