1

コンストラクターが以下のようなクラスがあります。

abstract class BasePanel extends JPanel {
  public BasePanel(A a) {
    // initializing fields from values passed to ctor
    this.a = a;
    // initializing gui components
    initializeComponents();
    setupPanels();
    concludeUiSetup();
  }

  // stuff
}

コンストラクターでは、最初にコンストラクターに渡された値で初期化されるフィールドが初期化されます。次に、UI セットアップに必要な他のメソッドが順番に呼び出されます。これらのメソッドのうち 2 つは、固有の UI セットアップのためにサブクラスでオーバーライドする必要があります。

FooPanelを拡張するクラスを考えてみましょうBasePanel。コンストラクターには、さらにいくつかの初期化パラメーターが必要です。

class FooPanel extends BasePanel {
  public FooPanel(A a, B b) {
    super(a);
    this.b = b;
  }

  @Override
  public void initializeComponents() {
    super.initializeComponents();
    // I require b here, but oops, b is not initialized at this point, and so 
    // this will throw NPE.
    someTextField.setText(b.get());
  }

  // stuff
} 

initializeComponentsメソッドはここで requiresbを必要としますが、残念ながらその時点では初期化されていません。

このコードを次のように再構成する適切な方法は何でしょうか。

  • 必要なフィールドは、必要になる前に設定されます。
  • (および他のパネル)を使用するコードはFooPanel、この変更によってあまり乱雑になりません。

どんな助けでも大歓迎です。ありがとう。

4

2 に答える 2

7

コンストラクターからオーバーライド可能なメソッドを呼び出さないでください。この場合にすべきことは、インスタンス フィールドのみを初期化するコンストラクターを定義し、GUI の初期化を、コンストラクターから呼び出されないオーバーライド可能な initialize() メソッドに入れることです。

したがって、FooPanel を作成するには、次のようにします。

FooPanel p = new FooPanel(a, b);
p.initialize();

FooPanel のすべてのクライアントにそれを強制しない場合は、コンストラクターをプライベートに定義し、ファクトリー メソッドを提供します。

public static FooPanel create(A a, B b) {
    FooPanel p = new FooPanel(a, b);
    p.initialize();
    return p;
}
于 2012-06-01T12:29:17.370 に答える
5

基本的に、コンストラクター内で仮想 (つまり、オーバーライド可能な) メソッドを呼び出さないようにしてください。まさにこの種の問題を引き起こします。コンストラクターで仮想メソッドを呼び出す場合は、それを文書化する必要があります。また、他の場所で呼び出すことはおそらく避けてください。このようなメソッドは、まだ完全に初期化されていないオブジェクトを処理するために作成する必要があり、扱いにくい場所に置かれます。

これ以上の情報がなければ、より具体的なアドバイスを知ることは困難ですが、可能な場合は継承よりも構成を採用することをお勧めします。または、少なくとも常にそれを考慮し、最もエレガントなアプローチを決定してください。

ここで本当に継承が必要な場合は、本当に必要initializeComponentsですか? サブクラスの状態から何も依存せずに、各クラスが独自のコンストラクター内で独自の初期化を行うことはできませんか?

于 2012-06-01T12:27:16.317 に答える