繁体   English   中英

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

[英]Java - Force subclasses to call super method after 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()必须是子构造函数的第一行,因此在调用super构造函数之前,我无法设置任何变量。

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

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

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

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

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

它现在可以正常工作,但是我发现在每个子类的构造函数的末尾调用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));

其中MyWrapper是的子类, SuperClass和包装的一个实例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...

}

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

您可以通过提供一个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(...));

虽然这主要是一种反模式,但应该备用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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