2012-03-14 9 views
2

私のコードの一部でエラーが見つかったと思いますが、すべての状況で頭が後ろを向いているようには見えません。私よりも賢い人が "はい、これは誤りです"と言うことができますが、私の実装のもう一つの選択肢を提案することは間違いありません。互いに参照する静的フィールドを初期化する際にエラーが発生する

エラーの原因は、2つのクラスの静的フィールドがどのように初期化されるかということです。他方のフィールドを参照して1つ(FooClass)を初期化し、タイプFooのオブジェクトを作成して1つ(MyUtility)を初期化します。ごめんなさい。説明は私の強みではありませんでした。

私はこの問題を軽減しようと努力していましたが、問題を実証しているようなものがありました。

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" 
    } 
} 

が、私はこのデザインは奇妙に見える実現するが、はるかにそれを正当化しようとしません(実際のコードが構成されている「奇妙」あまりにも、しかしなどさまざまな可視性を持つ別々のクラス内)。私はこれを行うためのより良い方法のための提案には間違いなく感謝します。

問題の要点(少なくともこのプログラムでは)は、FooClass.ITS_FOOフィールドが行Bが実行されるときにnullであるということです。私が線Aと線Bの順序を切り替えると、いずれのフィールドもnullになりません。

私はIn what order do static initializer blocks in Java run?のような質問を見ましたが、それもJava Language Specのどちらも、このような相互参照の初期化がどのように行われるかを記述していません。

残念なことに、このサンプルは実際の実装から削除されていますので、私はたぶん同じソリューションを翻訳するのに同じ時間を費やすことになりますが、それは説明をする価値があります。

答えて

4

はい、最初にFooClassを初期化すると、MyUtilityが初期化されます。 MY_FOOのイニシャライザは、FooClassがすでにこのスレッドで初期化されているため、処理が進められます。ですので、MY_FOOはnullではありません。

あなたはFooClassコンストラクタ(現在は単なる債務不履行)からITS_FOOを観察する一方、あなたはそれがヌルです参照してくださいよ...

この動作は十分スペックに記載ある - セクションあなたはすべての詳細を与えるにリンクしました - しかし、基本的にはの本当に悪い考えは、静的初期化子が互いに参照する2つの型を持つことです。微妙な方法でそれを修正しようとしないでください。依存関係を取り除く。私はこれが痛みであるかもしれないことに気がついていますが、実際には他の修正方法について考える価値はありません。 を固定行う

一つの方法は、他のタイプの両方が依存することができる静的任意他のタイプには依存しないイニシャライザ、及びと第三のタイプを抽出することができます。もちろん

は、あなたの静的初期化子で以下を行うことも有用です:)

+0

残念ながら(あなたの第三型の修正をWRT)、設計の基本的な部分は 'FooClass.ITS_FOO'フィールドは特別な種類のものということです'FooClass'です。私の例を簡略化すると、その事実が取り除かれました:(。しかし、私はまだクラスが初期化される前にコンストラクタを実行できることに驚いています(ただし、この場合は動作させるには魔法のように思えます)。いずれにしても、依存関係の1つを取り除く方法を考えます。これは悪い考え方のように聞こえます... –

+0

@Rob:これは12.4.2節のこのポイントでカバーされています:「CのClassオブジェクトが現在のスレッドによってCの初期化が進行中であることを示している場合、初期化のための再帰的な要求。LCをリリースし、正常に完了してください。 " –

+0

ああ!私は前にその言葉に従っていませんでしたが、今私はこの事件とその "完全に正常な"は誤解を招く... "本当に悪い考え"につながることがわかります。ありがとう! –

関連する問題