2009-10-02 43 views
2

眼下調用覆蓋的方法在一些Java代碼我有這樣的事情Java中,隱含

class A { 
void f() { 

} 
A() { 
    f(); 
} 
} 

class B extends A{ 
@Override 
void f() { 
    //do some stuff 
    super.f(); 
} 
} 

class C extends B { 
@Override 
void f() { 
    //do some stuff 
    super.f(); 
} 
} 

我想有f()打了個電話,然後通過每個父類迭代向上,運行覆蓋f()。我通過明確地呼叫super.f()這樣做,但我不想這樣做。

我這樣做的原因是後處理必須在達到A中的構造函數後完成。每個班級都有很多狀態正確的啓動,這就是爲什麼我們有上升趨勢f()的原因。

所以A構造是真的

A() { 
    //init some state in a 
    f(); //run f(), which will depend on the previous operation 
} 

如果我這樣做new C();我想C.f()叫,然後B.f()調用,那麼A.f()稱爲

無論如何,如果有這樣的一個更聰明的方式這,我對它開放。

回答

8

在構造函數中調用非最終方法通常是一個糟糕的主意 - 至少,我建議你將它記爲沉重。請記住,當調用f()時,C的構造函數將不會被調用 - 並且任何變量初始化器都不會被調用。該對象只有一半初始化,所以方法需要非常仔細地寫。

雖然在普通Java中沒有辦法隱式調用super.f()。鑑於我將在該代碼中加入大量警告,所以單個語句遠遠沒有達到世界末日:)

如果要驗證它是否已被調用,您可以始終檢查A的結果A.f()在調用之後立即構造函數 - 這將檢查該調用是否已達到最高級別。

+0

對於「在構造函數中調用非最終方法通常是一個糟糕的主意」(從經驗:-) – 2009-10-02 21:23:30

+0

我不需要任何構造函數已經運行,只要A的第一部分()被執行,我們狀態良好。這是通過使用私有名稱空間而不使用序列化接口來進行序列化的黑客攻擊的一部分。基本上所有類都事先聲明它們將要使用的變量,但是init是私有名稱空間。感謝Jon的回覆,我想這是不可能的,很高興有第二個意見 – Mike 2009-10-02 21:27:02

+0

但是,在構造函數中使用模板方法模式有時很有吸引力。只要抽象(或非最終)方法實現算法而不依賴於狀態,它就可以工作。另一個解決方案是使用延遲初始化。我有一個關於這個問題的博客:http://novyden.blogspot.com/2011/08/java-anti-pattern-constructors-and.html – topchef 2011-08-14 06:26:26

1

據我所知,沒有辦法做到這一點。 AspectJ可能會做類似這樣的事情,但是那樣你就不得不用一個註釋來標記它,我想,這跟調用super.f()幾乎沒什麼關係。您需要表示超類方法正在被調用,因爲您需要有能力分別爲每個子類做出決定 - 否則,您可能有一個根本不想將任何東西委派給超類的子類 - 所以默認是你只是放在代碼中。

4

這可能會有所幫助 - 這是一種強制子類調用super的方法;這將幫助您檢測開發失誤,其中一個子類的實施者忘了繼續通話了鏈super.f():

class A { 
boolean fDone; 

public final void f() { 
    fDone = false; 
    doF(); 
    if (!fDone) { 
    throw new IllegalStateException("super.f() was not called"); 
    } 
} 

protected void doF() { 
    //do some operation 
    fDone = true; 
} 
} 

class B extends A { 
protected void doF() { 
    //some operation, maybe repeat this pattern of using a flag to enforce super call 
    super.doF(); 
} 
} 

所以,孩子覆蓋DOF(),但需要調用super.doF();

+0

這是一個有用的建議,謝謝 – Mike 2009-10-02 21:31:26