簡體   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