Java-强制子类在构造函数之后调用超级方法

时间:2018-06-25 13:35:36

标签: java inheritance constructor

我希望一堆子类在完成如下构造函数后调用一个超级方法:

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

dispatch()函数包含对象创建后的一系列操作,该对象必须应用于所有子类。我不能将此函数移到超级构造函数中,因为它从需要已分配变量的子类中调用方法。但是由于super()必须是子构造函数的第一行,因此在调用超级构造函数之前,我无法设置任何变量。

它现在可以正常工作,但是我发现在每个子类的构造函数的末尾调用dispatch()是一个不好的想法。有没有更优雅的方式来解决这个问题?还是我应该完全重新考虑我的概念?

4 个答案:

答案 0 :(得分:4)

您的请求违反了几种Java最佳做法,例如:

  • 请勿在构造函数中进行复杂的配置,仅填充私有(最终)成员变量,并且仅执行非常基本的一致性检查(如果有的话)。

  • 请勿从构造函数中调用non privatenon final方法,甚至不能间接调用。

因此,我强烈建议您考虑一下类的设计。可能是,您的班级太多,并且有太多的责任感。

答案 1 :(得分:2)

  

它现在可以正常工作,但是我觉得打电话给他一个不好的想法   每个子类的构造函数结尾处的dispatch()。有没有   解决这个问题的更优雅的方式?还是我应该完全重新考虑我的   概念?

正如Timothy Truckle所强调的那样,您的构造函数逻辑太复杂了。

通过使用模板方法初始化子类实例,可以使事情变得更简单并达到目标。 请注意,您已经在doStuff()中使用了此模式。
子类构造函数确实是您在这里遇到的问题:您希望减少每个子类中所需的强制性样板,并提高其可读性和维护性。
因此,在超类中引入一个新的模板方法,并从超类的构造函数中调用它。
此方法将执行与构造函数相同的操作,但可以通过更灵活的方式进行调用。
dispatch()是一种人工方法,也不需要为技巧而引入。
整个逻辑可以从超类构造函数进行编排。

父类可能看起来像:

public abstract class Superclass {

    ...

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

    public abstract void init();

    public abstract void doStuff();
}

然后在子类中,替换:

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

作者:

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

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

结果要简单得多,因为此设计可以在一个地方(即超类构造函数)集中收集与子类的初始化“算法”相关的职责。


关于您的评论:

  

这确实比我做的还要优雅。谢谢!编辑:   刚刚注意到,这不适用于具有不同子类的子类   构造函数参数。知道如何解决这个问题吗?

有这样的要求,为了使事情简单明了,您必须分两步进行事情:

  • 实例化对象
  • 在引用上调用init()方法。

它看起来像:

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

这种方式的问题是您不确定init()方法是否被调用。您可以添加一个标志,每次在对象上调用方法时都要检查该标志。但这确实很麻烦且容易出错。避免那样。
为了改善这一点,我可能会使用包装模式。
您也可以使用拦截器/方面。但这不是一个好用例:init处理不是横向的,实际上与对象行为有关。使其可见更有意义。

使用包装纸,它看起来像:

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

MyWrapperSuperClass的子类,并包装SuperClass对象的实例:

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

}

答案 2 :(得分:0)

Lorelorelore是正确的,如果在调用该方法时确实需要完成任何子类实例化。否则,您可以做您所拥有的。如果其他人需要使用该代码,我建议提出足够的注释。

答案 3 :(得分:0)

您可以通过提供一种static方法来抽象使用您的SuperClass,该方法将执行您想要的代码,但还可以检查其是否已正确设置:

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

以及子类:

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

    public void doStuff(){
         ...
    }
}

然后可以这样执行:

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

尽管这主要是一种反模式,应该备用。