2010-05-25 53 views
19

類的構造函數運行後的方法比方說,我有一個Java類任何派生

abstract class Base { 
    abstract void init(); 
    ... 
} 

,我知道每一個派生類中都會有它的構造之後調用init()。我當然可以,只需調用它的派生類的構造函數:

class Derived1 extends Base { 
    Derived1() { 
     ... 
     init(); 
    } 
} 

class Derived2 extends Base { 
    Derived2() { 
     ... 
     init(); 
    } 
} 

但是這打破‘不重複自己’的原則不太好(也有將是Base很多子類)。當然,init()調用不能進入Base()構造函數,因爲它會執行得太早。

任何想法如何繞過這個問題?我也很高興看到一個Scala解決方案。

更新:這裏是工廠方法方法的通用版本:

interface Maker<T extends Base> { 
    T make(); 
} 

class Base { 
    ... 
    static <T extends Base> T makeAndInit(Maker<T> maker) { 
     T result = maker.make(); 
     result.init(); 
     return result; 
    } 
} 

更新2:這個問題基本上是「你如何使用模板方法構造函數」?答案似乎是,「你可以,但這是一個壞主意」。所以我可以做一個模板工廠(Template Method + Abstract Factory)。

+2

如果init()在基類中是抽象的,那麼爲什麼每個派生類都必須調用它?如果他們不這樣會怎樣? – 2010-05-25 17:38:04

+1

爲什麼不在'Base'類的構造函數中調用init()? – aioobe 2010-05-25 17:42:00

+0

@Skip Head:他們不會被正確初始化。 – 2010-05-25 17:52:21

回答

10

發生了什麼init()?很可能更好的設計可以完全消除該方法,或者至少放鬆它在子類的構造函數之後執行的要求。確保在構造函數完成之前init()不會使構造中的對象對任何其他線程可見,因爲這會產生併發錯誤。

作爲一個(醜陋的)替代,一個抽象方法可以由子類來實現爲僞構造函數:

abstract class Base { 
    Base() { 
    ctor(); 
    init(); 
    } 
    abstract void ctor(); 
    abstract void init(); 
} 
+0

事實上,這看起來比其他解決方案更好(醜陋,但比替代品難看)。 – 2010-05-25 18:26:13

10

避免這種情況。如果你這樣做,任何擴展你的類的類都可能決定調用init(),從而使對象處於不一致的狀態。

一種方法是讓init()方法由您的類的客戶端手動調用。有一個initialized字段,並拋出IllegalStateExcepion如果任何需要初始化的方法被調用沒有它。

一個更好的方法是使用一個靜態工廠方法代替構造函數:

public Derived2 extends Base { 
    public static Derived2 create() { 
     Derived2 instance = new Dervied2(); 
     instance.init(); 
     return instance; 
    } 
} 

更新:正如你在更新建議,您可以通過Builder靜態工廠方法,這將調用init()在實例上。如果你的子類很少,我認爲這是一個過度複雜的,但。

+1

這仍然讓我在工廠方法中分別調用'init()''Derived1','Derived2'等。 – 2010-05-25 18:14:25

+0

是的,你的builder版本是一個很好的補充。 – Bozho 2010-05-25 18:25:18

+0

重提你的最後一句話,從問題:「(並且會有很多Base的子類)」。 – 2010-05-25 18:32:52

6

除了Bozho的recommendation,應用程序容器非常適合這項任務。

javax.annotation.PostConstruct註釋標記init()方法,正確配置的EJB或Spring容器將在完成依賴注入之後,但在應用程序可以使用該對象之前執行該方法。

的實例方法:

@PostConstruct 
public void init() { 
    // logic.. 
} 

在企業應用程序,您可以在init()方法打開資源,例如文件系統。這個初始化可以拋出異常,不應該從構造函數中調用。

+0

出於好奇。爲什麼不應該從構造函數中拋出,而是從'init()'方法拋出? – Tarrasch 2016-08-12 10:42:22

1

或者使用彈簧......你可以做<beans default-init-method="init">,看到Default initialization and destroy methods。在Derived1構造函數/身體前

trait RunInit { 
    def init():Unit 
    init() 
} 

class Derived1 extends Base with RunInit { 
    def init() = println("INIT'ing!") 
} 

這將運行的init():

1

如果你是不利的,使用工廠由於某種原因,你可以使用下面的技巧。

+1

問題恰恰在於,我想'init'在Derived1構造函數/主體之後運行。 – 2010-05-25 19:59:44

2

如果Java有它,我們不會在野外看到所有這些init()方法調用。

「用子項環繞子構造函數」 - 不能在純java中完成。太糟糕了,因爲可能會有非常有趣的應用程序,特別是使用匿名類+實例初始化塊。

工廠和集裝箱 - 當原生new沒有完成工作時它們可能會有所幫助;但這是微不足道的和無聊的,並且不會與匿名類一起工作。