2012-03-14 42 views
2

我認爲我在我的一些代碼中發現了一個錯誤,儘管它在所有情況下似乎都沒有後退。我希望比我聰明的人可以明確地說「是的,這是一個錯誤」,更好的是,爲我的實施提出另一種替代方案。初始化引用彼此的靜態字段時出錯

我相信錯誤的來源是如何初始化兩個類的靜態字段;一個(在FooClass中)通過引用另一個字段進行初始化,另一個(在MyUtility中)通過創建Foo類型的對象進行初始化。對不起,聽起來不對。解釋從來不是我的強項。

我已經花了一天的更多時間嘗試減少問題,並且有可運行的東西來證明問題。

public class Tester { 

    static class FooClass { 
     static final FooClass ITS_FOO = MyUtility.MY_FOO; 
    } 

    static class MyUtility { 
     static final FooClass MY_FOO = new FooClass(); 

     static FooClass create() { 
      return new FooClass(); 
     } 
    } 

    public static void main(String[] args) { 
     System.out.println("utility's: " + MyUtility.create()); // Line "A" 
     System.out.println("class's: " + FooClass.ITS_FOO); // Line "B" 
    } 
} 

我實現這個設計看起來奇怪,但不會嘗試太多證明它(真正的代碼構成「奇怪」了,但是內單獨的班級,不同的可見性等)。我肯定會讚賞建議更好的方法來做到這一點。

問題的要點(至少在這個程序中)是當行B執行時FooClass.ITS_FOO字段是null。如果我切換A行和B行的順序,這兩個字段都不是null

我看過像In what order do static initializer blocks in Java run?這樣的問題,但似乎也沒有說明Java Language Spec如何描述這種相互參照初始化是如何完成的。

不幸的是,這個示例遠離我們的真實實現,我可能會花費相同的時間翻譯任何解決方案,但這將是值得的一些解釋。

回答

4

是的,如果你強制FooClass先初始化,那麼這將觸發MyUtility初始化。由於FooClass已在此線程中初始化,所以MY_FOO的初始化程序將繼續,因此MY_FOO將爲非空值。

在另一方面,如果從FooClass構造函數(目前只是默認的)觀察ITS_FOO,你會看到它的空那裏...

這種行爲在規範中有據可查 - 部分你已經鏈接到提供所有的細節 - 但基本上這是一個真的壞主意有兩種類型,其靜態初始化相互引用。不要試圖以微妙的方式修復它:擺脫依賴。我意識到這可能是一種痛苦,但實際上,不值得考慮任何其他修復。該固定執行

一種方式可以是提取第三類型具有靜態初始化,其不依賴於任何其他類型,並且其中兩個其他類型的可以取決於。

當然,在做你的靜態初始化少也有利於:)

+0

不幸的是(WRT您的第三種類型的修復),設計的基本組成部分是'FooClass.ITS_FOO'場是一種特殊的'FooClass'。簡化我的例子擺脫了這個事實:(。但是,我仍然感到驚訝的構造函數可以在類初始化之前執行(儘管在這種情況下它聽起來有點神奇)。無論如何,我會想辦法擺脫其中一個依賴;這聽起來像是一個壞主意...... – 2012-03-14 18:31:34

+0

@Rob:這一點從第12.4.2節的這一點開始:「如果C的Class對象表示當前線程正在對C進行初始化,那麼這必須是一個遞歸的初始化請求,釋放LC並正常完成。「 – 2012-03-14 18:32:28

+0

啊!我之前並沒有完全遵循這個措辭,但現在我發現這個案子和它的「完整正常」是誤導性的......導致了「非常糟糕的主意」。謝謝! – 2012-03-14 18:50:52