web-dev-qa-db-ja.com

Java:スーパークラスのメソッドシグネチャでサブクラスを返す

Fooの実装がいくつかあり、それに伴ってFooBuilderがいくつかある問題に取り組んでいます。 Fooは、設定する必要のあるいくつかの共通変数を共有しますが、特定の機能を実装するためにそれぞれのFooBuilderを必要とする個別の変数もあります。簡潔にするために、FooBuilderのセッターに次のようなメソッドチェーンを使用させたいと思います。

public abstract class FooBuilder {
  ...

  public FooBuilder setA(int A) {
    this.A = A;
    return this;
  }

  ...
}

そして

public class FooImplBuilder extends FooBuilder{
  ...
  public FooImplBuilder setB(int B) {
    this.B = B;
    return this;
  }
  public FooImplBuilder setC(int C) {
    this.C = C;
    return this;
  }
  ...
}

など、いくつかの異なるFooBuilder実装があります。これは技術的には私が望むすべてを実行しますが、このアプローチはメソッドチェーンが実行されるときのメソッド呼び出しの順序に敏感です。以下に、メソッドの未定義のコンパイルエラーがあります。

someFoo.setA(a).setB(b)...

開発者がチェーン内のメソッド呼び出しの順序について考える必要があります。これを回避するために、FooBuilderのセッターに実際の実装サブクラスを何らかの方法で返してもらいたいと思います。ただし、これを行う方法がわかりません。最善のアプローチは何ですか?

25
downer

これは良い質問であり、本当の問題です。

Javaでそれを処理する最も簡単な方法は、Jochenの回答で述べられているように、ジェネリックスの使用を伴う可能性があります。

Fluent Interfacesでの継承の使用 に関するこのブログエントリには、この問題と合理的な解決策についての良い議論があります。これは、ジェネリックスとビルダーサブクラスでオーバーライドされたgetThis()メソッドの定義を組み合わせたものです。常に正しいクラスのビルダーを返すという問題を解決します。

19
Don Roby

ジェネリックはここに行く方法かもしれません。

SetA()を次のように宣言すると(擬似コード)

<T> T setA(int a)

コンパイラは実際の型を理解できるはずです。そうでない場合は、次のようなコードでヒントを与えることができます。

obj.<RealFoo>setA(42)
4
Jochen

見つけた この優れた答え 私は今それを共有しています。

public class SuperClass<I extends SuperClass>
{
    @SuppressWarnings( "unchecked" ) // If you're annoyed by Lint.
    public I doStuff( Object withThings )
    {
        // Do stuff with things.
        return (I)this ; // Will always cast to the subclass. Causes the Lint warning.
    }
}

public class ImplementationOne
extends SuperClass<ImplementationOne>
{} // doStuff() will return an instance of ImplementationOne

public class ImplementationTwo
extends SuperClass<ImplementationTwo>
{} // doStuff() will return an instance of ImplementationTwo
4
zerobandwidth