3

このインターフェースと具象があるとします:

public interface MeterValue {}
public class MeterValueA implements MeterValue {}
public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {} 
public class ReportBuilderA implements ReportBuilder<MeterValueA> {}
public class ReportBuilderB implements ReportBuilder<MeterValueB> {}

そして、私はグローバルインスタンスを持っています:

ReportBuilder<MeterValue> reportBuilder;

reportBuilder インスタンスを次のように変更したいと思います。

if (isA())
   reportBuilder = new ReportBuilderA();
else
   reportBuilder = new ReportBuilderB();

reportBuilder.desiredMethod();

これを行うと、「タイプミスマッチ」が発生します。希望する機能を取得するにはどうすればよいですか? または、クラスの設計をより柔軟に改善するにはどうすればよいでしょうか?

編集:

ワイルドカード ? 問題を解決しました。しかし、他の問題ももたらしました。インターフェイスにこのメソッドがあるとします:

public interface ReportBuilder<T extends MeterValue> {
     public String getStuff(List<T> values);
} 

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
     @Override
     public String getStuff(List<MeterValueA> values) { return "A"; }
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
     @Override
     public String getStuff(List<MeterValueB> values) { return "B"; }
}

このインスタンスがあるとします:

  ReportBuilder<? extends MeterValue> reportBuilder = new ReportBuilderA();

と:

  List<? extends MeterValue> list = new ArrayList<MeterValueA>();
  String s = reportBuilder.getStuff(list);

リストにワイルドカードが含まれているため、getStuff(...) の呼び出しは機能しません。修正の 1 つは、ReportBuilder インターフェイス メソッドを次のように変更することです。

public String getStuff(List<? extends MeterValue> values);

しかし、具体的な ReportBuilders を壊します。(リストは、内容が異なる場合があるグローバル インスタンスでもあります)

何か案は?

4

2 に答える 2

3

レポートビルダー変数を次のように宣言する必要があると思います。

ReportBuilder<? extends MeterValue> reportBuilder;

これにより、ジェネリック型の、またはインターフェイスを実装する任意のクラスreportBuilderを持つ任意のクラスを持つことができます。ReportBuilderMeterValue

ちなみに、まだ慣れていない場合は、インスタンスの作成はファクトリメソッドパターンReportBuilderに似ているように思われるので、一読する価値があるかもしれません。(そしてもちろん他のパターンも。)

于 2013-02-01T09:14:21.537 に答える
3
public interface MeterValue {}

public class MeterValueA implements MeterValue {}

public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {
    void desiredMethod();
}

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
    @Override
    public void desiredMethod() {}
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
    @Override
    public void desiredMethod() {}
}

void f() {
    ReportBuilder<? extends MeterValue> reportBuilder = null;

    if (Math.random() > 0.5)
        reportBuilder = new ReportBuilderA();
    else
        reportBuilder = new ReportBuilderB();

    reportBuilder.desiredMethod();
}

ここで、:ReportBuilder<? extends MeterValue>ジェネリックパラメーターがMeterValueインターフェースを拡張できることを意味します。

ワイルドカード宣言の詳細については、チュートリアルを参照してください。


注:並列継承の匂いを避けるために、特定のケースでこのコードを確認してください。新しいMeterValueを追加する場合は、ReportBuilderを追加する必要があります。これは、おそらくこの問題があることを意味します。


編集:

MeterValueのサブタイプを持つ配列を、少なくともMeterValueAを必要とするReportBuilderAに渡すことはできません。型安全技術ではありません。C ++には、継承後に具象クラスをインスタンス化する方が適切です。Javaでは、継承後に制限付きタイプを変更しない方がよいでしょう。

考えられる解決策、具体的なケースに適用できる可能性があります。

1)MeterValueには単一のインターフェースを使用します。すべての違いはReportBuilderの継承に移行します。これは、プロジェクトに並列継承があることを意味します。

2)メソッドList<MeterValueA>を呼び出すときはconcreteを使用します。処理および署名付きのメソッドへの移動getStuff()のためのすべての共有ロジック:List<MeterValueA>List<MeterValueB>public void sharedLogic(List<? extends MeterValue> list)

于 2013-02-01T09:15:33.130 に答える