2016-04-11 3 views
2

私は、抽象メソッド(doSetup)を持つ抽象クラス(Parent)と、doSetupメソッドを呼び出すメンバーメソッドを持っています。私が必要とするのは、子クラスが持つ可能性のある多くのコンストラクタにかかわらず、自動的にdoSetupメソッドを呼び出す子クラス(実装するParent)です。これを解決するのに役立つJavaのメカニズムやデザインパターンがありますか?すべての子コンストラクタで親メソッドの実行を自動的にインクルードする方法は?

public abstract class Parent { 
    abstract protected void sayHi(); 
    protected void doSetup() { 
    sayHi(); 
    } 
} 

public class Child1 extends Parent { 
    @Override 
    protected void sayHi() { 
    System.out.println("hi"); 
    } 
    public Child1() { 
    // Construction needs to automatically include exec of doSetup() 
    } 
    public Child1(String string) { 
    // Construction needs to automatically include exec of doSetup() 
    System.out.println("another constructor"); 
    } 
} 
+1

基本クラスのコンストラクタでdoSetup()メソッドを呼び出すだけです。 s。その後、特定のメソッドに基本クラスの機能を組み込む場合は、 'super.methodYouWishToCall()'だけを使用してください。 – ManoDestra

+0

私が得ようとしているのは自動インクルージョンです。そのため、他の開発者が 'Parent'を実装すると、' doSetup'呼び出しをコンストラクタに含めることを忘れる機会はありません。 – yamori

+2

私が言ったように、あなたのベースクラスのコンストラクタでそれを強制します。次に、サブクラスがインスタンス化されると、基本クラスのコンストラクターが呼び出され、そのメソッドが呼び出されます。私がここで何を意味するのか分からないなら、私はあなたの答えとしてそれを書くことができます。 – ManoDestra

答えて

0

親コンストラクタにdoSetup()を含めないのはなぜですか?例えば、明示的にスーパーコンストラクタを呼び出す必要があります次に、あなたのサブクラスコンストラクタのそれぞれを

public abstract class Parent { 
    final String greeting; 

    public Parent(String greeting) { 
    this.greeting = greeting; 
    doSetup(); 
    } 

    final void doSetup() { 
    System.out.println(greeting); 
    } 
} 

:コンストラクタからoverrideableメソッドを呼び出さないようにするには、あなたのパターンビットを変更することができ

public class Child1 extends Parent { 
    private static String default_greeting = "hi"; 

    public Child1() { 
    super(default_greeting); // prints "hi" 
    } 

    public Child1(String string) { 
    super(string); // print a different greeting 
    } 
} 
2

良いIDEはおそらくコンストラクタでオーバーライド可能なメソッドを使用することに対して警告します。

理由は、おそらく驚くべき結果をもたらす次のコードで説明できます。動作を制御する

class Base { 
    Base() { 
     init(); 
    } 

    protected void init() { 
    } 
} 
class Child extends base { 
    String a = "a"; 
    String b; 
    String c = "c"; 
    String d; 

    public Child() { 
     // 1. Fields are nulled 
     // 2. super() called 
     // 2.1. init() called 
     // 3. Field initialisations done (a, c) 
     // 4. Rest of constructor: 
     System.out.printf("EndConstr a: %s, b: %s, c: %s%n", a, b, c); 
    } 

    @Overridable 
    protected void init() { 
     System.out.printf("Init a: %s, b: %s, c: %s%n", a, b, c); 
     c = "cc"; 
     d = "dd"; 
    } 
} 

溶液は、指定された方法でオーバーライド保護されたメソッドを呼び出す1つの最終非オーバーライドメソッドを提供することである。

class Base { 
    public final void f() { 
     X x = ...; 
     onF(x); 
    } 
    protected /*abstract*/ void onF(X x) { 
    } 
} 
class Child extends Base { 
    @Overridable 
    protected void onF(X x) { 
     ... 
    } 
} 
+1

別の側面があります。コンストラクタで 'init()'を呼び出すと、構築時に呼び出されますが、オーバーライドできるようにすると、サブクラスが 'super.init()'を呼び出さない実装とオーバーライドする可能性があります。その呼び出しはもう強制されません。 – Holger

+0

この回答は、コンストラクタでオーバーライド可能なメソッドを呼び出さないように私に納得させました。上記の最初の例では、構築/インスタンス化の順序がオーバーライドされたメソッドを取得することを示していますが、 'child 'のインスタンス変数の値は、コンストラクタが' init()'を呼び出すときに設定されません – yamori

+0

@Joop Eggen、あなたのタイトルに二重否定を持たせることを意味していなかったことを確認するだけで、 "...重複可能なメソッドを使用して警告します..."対 "...重複可能なメソッドを使用しないよう警告します..." – yamori

2

これは共通実装する1つの方法であります建設コード:

Parent.java

public abstract class Parent { 
    public Parent() { 
     this("Default Value Goes Here"); 
    } 

    // Funneling everything through this main constructor. 
    public Parent(String value) { 
     this.doSetup(value); 
    } 

    // I've made this method private, as it shouldn't really 
    // be accessed from sub classes, but if you require that, then 
    // mark this method as protected & final instead. 
    private void doSetup(String value) { 
     System.out.println(value); 
    } 
} 

Child.java

public class Child extends Parent { 
    // Deliberately not implementing constructors here, 
    // but if I did, the first call would be to a super() 
    // constructor to retain parent's construction functionality. 
} 

MainApp.java

public class MainApp { 
    public static void main(String[] args) { 
     Child child = new Child(); 
    } 
} 

実行MainAppに上記とデフォルト値のコンストラクタが実行されていることがわかりますし、それが出力する「デフォルト設定されますそれは親のコンストラクタによって強制されるため、ここでは「Value Goes Here」となります。

0

他にも述べたように、スーパークラスのコンストラクタ内からオーバーライド可能なメソッドを呼び出すべきではありません。これは、this to escapeを回避し、最終的には難しい競合状態を引き起こすか、またはオーバーライドされたメソッド内からまだ初期化されていないフィールドにアクセスしている間に、NullPointerExceptionがスローされるのを避けるためです。あなたはすべてのcontructorsに共通のコードを実行したい場合

さて、質問に関しては、何が必要initializer blockです:

new Child1(); 
new Child1("something"); 

が生成されます

public abstract class Parent { 
    { 
     // this is an initializer block that is inherited 
     // by all subclasses and runs for every constructor 
     // in the hierarchy 
     this.doSetup(); 
    } 

    protected final void sayHi() { // final to avoid this to escape 
     System.out.println("hi"); 
    } 

    protected final void doSetup() { // final to avoid this to escape 
     sayHi(); 
    } 
} 

public class Child1 extends Parent { 

    public Child1() { 
     // initilizer block automatically called 
    } 

    public Child1(String string) { 
     // initilizer block automatically called 
     System.out.println("another constructor"); 
    } 
} 

は、このテストを実行します次の出力:

hi 
hi 
another constructor 
関連する問題