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

Java-强制子类在构造函数之后调用超级方法,java,inheritance,constructor,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

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

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.
    }
}
SuperClass.executeAnActionOnSuperClass(new SubClass(...));
dispatch()
函数包含一组在对象创建后要处理的事情,必须应用于所有子类。我不能将这个函数移到超级构造函数中,因为它调用子类中的方法,这些子类需要已经分配的变量。但是由于
super()
需要作为子构造函数的第一行,因此在调用超级构造函数之前,我无法设置任何变量


它将像现在一样工作,但我发现在每个子类的构造函数末尾调用
dispatch()
是一个糟糕的概念。有没有更优雅的方法来解决这个问题?或者我应该完全重新思考我的概念吗?

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

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

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

  • 不要从构造函数调用
    非私有
    非最终
    方法,甚至不要间接调用

所以我强烈建议你考虑一下你的课程设计。您的类可能太大,职责太多。

您可以通过提供一个执行所需代码的
静态
方法来抽象超类的使用,但也可以检查它是否已正确设置:

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(){
         ...
    }
}
然后可以这样执行:

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.
    }
}
SuperClass.executeAnActionOnSuperClass(new SubClass(...));
虽然这主要是一种反模式,应该少用

它会像现在这样工作,但我觉得这是一个糟糕的概念 dispatch()位于每个子类的构造函数末尾。有没有 更优雅的解决方法?或者我应该彻底重新思考我的生活吗 概念

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

通过使用template方法初始化子类实例,您可以使事情变得更简单并达到您的目标。 请注意,您已经将此模式用于
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.
}
作者:

结果要简单得多,因为这种设计将与子类的初始化“算法”相关的责任集中在一个地方:超级类构造函数


关于你的评论:


这看起来确实比我做的要优雅得多。非常感谢。编辑: 刚才注意到,这不适用于具有不同 构造函数参数。你知道怎么解决这个问题吗

有了这样的要求,要使事情变得简单明了,你必须分两步来做:

  • 实例化对象
  • 在引用上调用
    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...

}

你不能,这是个坏主意。如果您使用
子类
再次对
子类
进行子类化,会怎么样?然后在调用
SubSubClass
的构造函数之前调用
dispatch
,如果
SubSubClass
覆盖了
doStuff
,那么该方法将在初始化类之前被调用。这看起来确实比我做的要优雅得多。非常感谢。编辑:刚刚注意到,这不适用于具有不同构造函数参数的子类。你知道如何解决这个问题吗?但这违背了一个事实,即你不能从超类构造函数调用可重写的方法,对吗?@user7 right:这通常是不建议的,但在这里
init()
替换了子类的构造函数。因此,在子类实例上调用方法不会有风险,因为子类实例没有调用其构造函数,因此子类实例的状态不一致。