如何在所有子构造函数中自动包含父方法的执行?

时间:2016-04-11 15:18:48

标签: java oop design-patterns java-8

我有一个抽象类(Parent),它有一个抽象方法(doSetup),以及一个调用doSetup方法的成员方法。我需要的是构造中的子类(实现Parent)应该自动调用doSetup方法,无论子类可能有多少构造函数。是否有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");
  }
}

4 个答案:

答案 0 :(得分: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";
    }
}

控制行为的解决方案是提供一个最终的不​​可覆盖的方法,该方法以指定的方式调用可覆盖的受保护方法:

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 :(得分:2)

这是实现常见构造代码的一种方法:

<强> Parent.java

try {
        Response response = client.newCall(request).execute();

        String rep = response.body().string();
        if(rep.length() > 0){
            Log.i(TAG, "Got Response");
            Log.i(TAG, rep);
        }

    }catch (Exception e){
        e.printStackTrace();
    }

<强> Child.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);
    }
}

<强> MainApp.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,您将看到默认值构造函数已运行,它将输出“Default Value Goes Here”,因为它是由父级构造函数强制执行的。

答案 2 :(得分:0)

为什么不在父构造函数中包含doSetup()?为了避免从构造函数中调用可重写方法,您可以稍微改变一下模式:

public abstract class Parent {
  final String greeting;

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

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

然后你的每个子类&#39;构造函数需要显式调用超级构造函数,例如:

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
  }
}

答案 3 :(得分:0)

正如其他人所说,你不应该在超类的构造函数中调用一个可覆盖的方法。这是为了避免this to escape并最终产生难以调试的竞争条件,或者只是在从覆盖方法中访问尚未初始化的字段时避免抛出NullPointerException

现在,关于这个问题,如果你想为所有的构造者运行公共代码,你需要的是initializer block

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");
    }
}

运行此测试:

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

产生以下输出:

hi
hi
another constructor