2

I have an abstract class (Parent) which has an abstract method (doSetup), and a member method which calls the doSetup method. What I need is the child class (which implements Parent) at construction should automatically call the doSetup method, regardless of however many constructors the child class might have. Is there a Java mechanism or a design pattern which could help me solve this?

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");
  }
}
yamori
  • 1,213
  • 4
  • 14
  • 27
  • 1
    Simply call the doSetup() method in your base class' constructors. And if you wish to include the base class' functionality for a particular method thereafter, just use `super.methodYouWishToCall()`. – ManoDestra Apr 11 '16 at 15:21
  • What I'm trying to get is automatic inclusion, so when other developers implement `Parent` they won't have the opportunity to forget to include the `doSetup` call in their constructors. – yamori Apr 11 '16 at 15:23
  • 2
    As I said, enforce that in your base class' constructors. Then, when any subclasses are instantiated, the base class' constructor will have to be called, enforcing that method to be called. I can write it up as answer for you, if you're not sure of what I mean here. – ManoDestra Apr 11 '16 at 15:24
  • 4
    @yamori This sounds like a good idea, but in practice it isn't. You should never call overridable methods from a constructor. This is explained well in Effective Java. – Paul Boddington Apr 11 '16 at 15:25
  • 1
    Agreed. It wouldn't be overridable. It would have to be private or final, in my view. If it's code that must be common and not overridden by subclasses. – ManoDestra Apr 11 '16 at 15:34
  • @ManoDestra, my apologies, I misread your reply. And you're right, it does work. I had a misconception that putting that in the parent's/base's constructor would not work. I would mark your answer as correct, but am still interested about design discussion. – yamori Apr 11 '16 at 15:35
  • 1
    I'll write up an answer for you. – ManoDestra Apr 11 '16 at 15:35

4 Answers4

2

A good IDE probably will warn against using overridable methods in the constructor.

The reason can be demonstrated with the following code that has maybe surprising results.

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

A solution to control behavior would be to offer one final non-overridable method that calls an overridable protected method in a specified way:

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) {
        ...
    }
}
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    There’s another aspect. Invoking `init()` in the constructor makes sure that it will be called upon construction, but allowing it to be overridden opens the possibility that a subclass overrides it with an implementation that doesn’t invoke `super.init()`, so it’s invocation isn’t enforced anymore… – Holger Apr 11 '16 at 17:24
  • This answer convinced me not to call an overridable method in the constructor. For those wondering, the first example above demonstrates that the order of construction/instantiation gets the overriden method, but `Child's` instance variables' values are not set in time for the the constructor's call to `init()` – yamori Apr 12 '16 at 20:04
  • @Joop Eggen , just confirming that you didn't mean to have a double negative in your title, should read "... warn against using overridable methods..." vs. "... warn against not using overridable methods..." – yamori Apr 12 '16 at 20:06
2

This is one way to implement common construction code:

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

Run the MainApp above and you will see that the default value constructor is run and it will output "Default Value Goes Here", as it is enforced by the parent's constructor.

ManoDestra
  • 6,325
  • 6
  • 26
  • 50
0

Why not include doSetup() in the parent constructor? To avoid calling an overrideable method from the constructor, you could alter your pattern a bit:

public abstract class Parent {
  final String greeting;

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

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

Then each of your subclasses' constructors will need to explicitly call the super constructor, for instance:

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
  }
}
TayTay
  • 6,882
  • 4
  • 44
  • 65
0

As others have stated, you shouldn't call an overridable method from within the constructor of a super class. This is to avoid this to escape and eventually produce hard-to-debug race conditions or to just avoid NullPointerExceptions to be thrown while accessing a yet uninitialized field from within overriden methods.

Now, with regard to the question, if you want to run common code to all contructors, what you need is an 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");
    }
}

Running this test:

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

Produces the following output:

hi
hi
another constructor
Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110