简体   繁体   中英

Java - Force subclasses to call super method after constructor

I want a bunch of subclasses to call a super method after finishing the constructor like this:

public abstract class Superclass {

    ...

    public Superclass(...) {
        ...    // do stuff before initializing subclass
    }

    protected void dispatch() {     //method to be called directly after creating an object
        doStuff();
        ...
    }

    public abstract void doStuff();
}

public class Subclass extends Superclass {

    ...

    public Subclass(...) {
        super(...);     //has to be the first line
        ...             //assign variables etc.
        dispatch();     //has to be called after variables are assigned etc.
    }

    public void doStuff() {
        //do stuff with assigned variables etc.
    }
}

The dispatch() function contains a set of things to do with the object after it has been created, which has to apply to all subclasses. I can't move this function into the super constructor, since it calls methods from the subclasses which require already assigned variables. But since super() requires to be the first line of the sub constructor though, I can't set any variables until after the super constructor was called.

It would work as it is now, but I find it a bad concept to call dispatch() at the end of every subclasses' constructor. Is there a more elegant way to solve this? Or should I even completely rethink my concept?

Your request violates several Java best practices, eg:

  • Do not do complex configuration in a constructor, only fill private (final) member variables and do only very basic consistency checks (if any at all).

  • Do not call non private or non final methods from the constructor, not even indirectly.

So I would strongly suggest to think over the design of your classes. Chances are, that your classes are too big and have too many responsibilitis.

It would work as it is now, but I find it a bad concept to call dispatch() at the end of every subclasses' constructor. Is there a more elegant way to solve this? Or should I even completely rethink my concept?

As underlined by Timothy Truckle, your constructor logic is too complex.

You can make things much simpler and reaching your goal by using the template method to initialize the subclass instance. Note that you already used this pattern with doStuff() .
The subclass constructor is indeed your issue here : you want to reduce the mandatory boiler plates required in each subclasss and also make their readability and maintenance better.
So introduce a new template method in the superclass and invoke it from the constructor of the super class.
This method will do the same thing as a constructor but could so be invoked in a more flexible way.
dispatch() that is an artificial method, introduced only for the trick is not required either.
The whole logic could be orchestrated from the super class constructor.

The super class could look like :

public abstract class Superclass {

    ...

    public Superclass(...) {
        ...    // do stuff before initializing subclass
        init();
        doStuff();
    }

    public abstract void init();

    public abstract void doStuff();
}

And in the subclass, replace :

public Subclass(...) {
    super(...);     //has to be the first line
    ...             //assign variables etc.
    dispatch();     //has to be called after variables are assigned etc.
}

by :

public Subclass(...) {
    super(...);   // let the super constructor to orchestrate the init logic  
}

public void init(){
    // move the constructor logic here
}

The result is much simpler because this design gathers responsibilities related to the initialization "algorithm" of the subclass in a single place : the super class constructor.


About your comment :

This indeed looks way more elegant than what I did. Thank you! EDIT: Just noticed, this does not work with subclasses having different constructor parameters. Any idea how to solve this?

With such a requirement, to make things simple and clear, you have to make things in two steps :

  • instantiate the object
  • invoke on the reference the init() method.

It could look like :

SuperClass o = new Subclass(argFoo, argBar); 
o.init();

The problem with this way is that you are not sure that the init() method was invoked. You could add a flag that you check at each time a method is invoked on the object. But it is really cumbersome and error-prone. Avoid that.
To improve that, I would probably use the wrapper pattern.
You could also use an interceptor/aspect. But it is not a good use case : the init processing is not transversal and is really related to the object behavior. It makes more sense to keep it visible.

With a wrapper, it could look like :

SuperClass o = new MyWrapper(new Subclass(argFoo, argBar));

Where The MyWrapper is a subclass of SuperClass and wraps an instance of SuperClass object :

public class MyWrapper implements SuperClass{

   private SuperClass wrapped;

   public MyWrapper (SuperClass wrapped){
       this.wrapped = wrapped;
       this.wrapped.init();
   }

   // then delegate each superclass method to the wrapped object
   public void doStuff(){
       this.wrapped.doStuff();
   }

  // and so for...

}

Lorelorelore is correct, if you do need any subclass instantiation to be complete when the method is called. Otherwise, you can do what you have. I would suggest putting adequate comments if others will need to use the code.

You can abstract the use of your SuperClass by providing a static method which will execute the code you want, but also checks that it has been setup correctly:

public abstract class SuperClass{
    private boolean instantiated;

    public SuperClass(...){
        ...
    }

    public abstract void doStuff();

    private void dispatch(){
        if(!instantiated){
            instantiated = true;
            doStuff();
        }
    }

    public static void executeActionOnSuperClass(SuperClass s){
        s.dispatch(); // call instantiation if not already done
        s.executeAnAction();
    }
}

And the subclass:

public class SubClass extends SuperClass{
    public SubClass(...){
        super(...);
    }

    public void doStuff(){
         ...
    }
}

Which then can be executed like this:

SuperClass.executeAnActionOnSuperClass(new SubClass(...));

Though this is mostly an anti pattern and should be used sparely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM