web-dev-qa-db-ja.com

サブクラスにJavaで変数を設定させる方法は?

特定の画面のすべての基本パラメータを定義するクラスがあります。ここから、アプリケーションのすべての画面がこのサブクラスになります。実装で変数の値を設定するには、every screen(つまり、サブクラス)が必要です(つまり、各画面でナビゲーションツリーのレベルを定義する必要があります)。

また、理想的には、この変数がサブクラスに設定されている場合はfinalである必要があります(これはおそらく不可能だと思います)。

これについて行くための最良の方法は何ですか? Javaでこのタイプの動作を正しく実施する方法はありますか?

19
B T

@pstのコメントがこの解決策につながります。

これは変数では実行できません。ただし、抽象クラスでは、特定のメソッドの実装が必要になる場合があります。このメソッドは、該当する値を返す可能性があります。

abstract関数を宣言して変数を設定または返すことから、任意のサブクラスにそれを正しく実装させることができます。

次に、関数は外部クラスのすべてのサブクラスによって呼び出される必要があります。これは、外部クラスのどこかで実行する必要があることを意味します。これは、サブクラスがsuperを呼び出すことを心配することなく、外部クラスの引数なしのコンストラクターで実行できます。

注:コンストラクターがスーパークラスコンストラクターを明示的に呼び出さない場合、Javaコンパイラーは、スーパークラスの引数なしコンストラクターへの呼び出しを自動的に挿入します。スーパークラスに引数なしがない場合コンストラクターの場合、コンパイル時エラーが発生します。Objectにはそのようなコンストラクターがあるため、Objectが唯一のスーパークラスであれば、問題はありません。 (Java docs:Super)

これに基づいて、このソリューションは、次のいずれかである限り、設定する変数を保持し、正しくforceします。

  1. スーパークラスには他のコンストラクターは作成されません(したがって、サブクラスでスーパーを使用して別のコンストラクターを呼び出すことはできません)
  2. スーパークラスの他のすべてのコンストラクターは、引き続きデフォルトのコンストラクターを内部的に呼び出します

コード:

スーパークラス:

public abstract class SuperClass {
    // Variable all inner classes must set
    static int myVar = 0;

    public SuperClass() {
        myVar = giveValue();
    }

    public abstract int giveValue();
}

サブクラス:

public class SubClass extends SuperClass {

    @Override
    public int giveValue() {
        return 5; // individual value for the subclass
    }

}
31
B T

子クラスインスタンスがフィールドを初期化するように強制するのではなく、親クラスコンストラクターに、初期化するフィールドを提供するインターフェイスを実装するパラメーターを取得させることで、構成の戦略に従うことができます。

class Screen {
  final Configuration config;
  Screen(Configuration config) {
    this.config = config;
  }
  // or
  Screen(ConfigFactory configFactory) {
    this.config = configFactory.make();
  }
}

interface ConfigFactory {
  Configuration make();
}

抽象メソッドの実装を使用するなど、構成を初期化するサブクラスインスタンスを要求しないように注意します。親クラスコンストラクターでの割り当ては、サブクラスインスタンスが初期化される前に発生し、暗黙的に構成の適切な計算を静的にします。

計算が静的でない場合、あなたをフォローしている開発者(またはメモリが完全ではない場合は自分自身)によるnull参照またはNullPointerExceptionsのリスクがあります。共同作業者(および自分自身)の作業を容易にし、制約を明示的にします。

11
jlb

@BTの回答で@Ketanが述べたように、コンストラクターからオーバーライド可能なメソッドを呼び出すことは、特に良い習慣ではありません( https://help.semmle.com/wiki/display/Java/Non-final+method + invocation + in +コンストラクター

この問題を回避する1つの方法は、フィールドの抽象(保護された)ゲッターを用意することです。したがって、スーパークラスにはフィールドがありませんが、ゲッターを使用してスーパークラスからアクセスできます。各サブクラスは、抽象ゲッターをオーバーライドする必要があるため、フィールドを宣言する必要があります。

スーパークラス:

public abstract class SuperClass {

  public SuperClass() {}

  protected abstract int getMyVar();

  public void functionUsingMyVar(){
     int a = 12 + getMyVar();
  }
}

サブクラス1:

public class SubClass1 {

  private int myVar;

  public SubClass1() {
     super();
     myVar = 1;
  }

  @Override
  protected int getMyVar(){
     return myVar;
  }

}

サブクラス2:

 public class SubClass2 {

  private int myVar;

  public SubClass2() {
     super();
     myVar = 1;
  }

  @Override
  protected int getMyVar(){
     return myVar;
  }

}

スーパークラスを使用する代わりに(giveValue()はオーバーライド可能で、コンストラクターで呼び出されます):

public abstract class SuperClass {

  private int myVar;

  public SuperClass() {
     myVar = giveValue();
  }

  protected abstract int giveValue();

  public void functionUsingMyVar(){
     int a = 12 + myVar;
  }
}
1
Lulu