2009-04-27 12 views
5

私はSetクラスを持っています(これはJ2MEなので、私は標準APIへのアクセスが制限されています;クラスとサブクラスで一定のセットを作成するために、私はsetクラスを使用しています。これは、このような外観です...静的イニシャライザをJavaで実行する順序を保証できますか?

class ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("four"); 
     add("five"); 
     add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    }}; 
} 

[1]の行を除いて、すべてがうまく見えますが、nullポインタ例外が発生します。おそらく、これは、サブクラスの静的な初期化子が親クラスの前に実行されていることを意味します。これは、instatiatedサブクラスで実行する前に、最初に新しいインポートで静的ブロックを実行すると思っていたので、私を驚かせました。

私はこの仮定の通りですか?この動作を制御または回避する方法はありますか?

更新:

物事はさえ見知らぬ人です。私は、これは代わりに(「新しいParentClass()」行に注意してください)試してみました:

class ParentClass 
{ 
    public ParentClass() 
    { 
     System.out.println(THE_SET); 
    } 

    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     System.out.println("a"); 
     new ParentClass(); 
     System.out.println("b"); 
     add("four"); 
     System.out.println("c"); 
     add("five"); 
     System.out.println("d"); 
     add("six"); 
     System.out.println("e"); 
     union(ParentClass.THE_SET); /* [1] */ 
     System.out.println("f"); 
    }}; 
} 

、出力は奇妙です:

a 
["one", "two", "three"] 
b 
c 
d 
e 
Exception in thread "main" java.lang.ExceptionInInitializerError 
Caused by: java.lang.NullPointerException 

だからParentClassが初期化されますが、サブクラスがアクセス権を持っていませんその静的なイニシャライザ内にあります。

答えて

7

これは達成しようとしていることですか?または、Setインターフェイスのローカル実装が必要ですか? [「1」、「2」、「3」]の出力ラインを考えると

class ParentClass 
{ 
    protected final static Set THE_SET; 

    static { 
     THE_SET = new HashSet(); 
     THE_SET.add("one"); 
     THE_SET.add("two"); 
     THE_SET.add("three"); 
    } 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SECOND_SET; 

    static { 
     THE_SECOND_SET = new HashSet(); 
     THE_SECOND_SET.add("four"); 
     THE_SECOND_SET.add("five"); 
     THE_SECOND_SET.add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    } 
} 
+0

それも同じことです。 – izb

+1

私はエリヤが正しいと思う。これは初期化順序の問題ではなく、むしろ一種の名前の衝突です。 – boutta

+0

実際のコードでは、実際にはA <-B <-Cの3つのクラスがあります。興味深いことに、各クラスに独自のセット名を与えるとCに例外が移りますが、問題は解決しません。これは単に注文が実際には予測できないことを意味します。 – izb

3

クラス間で静的な初期化子順序の保証はありません。クラス内では、ソースコードの順に実行されます。

考えてみると、クラスがロードされる時期を制御していないため、therreはクラスのうちの1つになりません。クラスを動的にロードするか、JVMがロード順序を最適化する可能性があります。

+0

クラスAがクラスBを拡張する場合、クラスBをロードする前にクラスAを完全にロードする必要はありませんか? –

+0

私はそれが別の方法かもしれないと思っていただろう。確かにAはそこにあるBに依存するので、Bを最初に初期化する必要があります。 – izb

+0

しかし、(またはBについて知るには)Aのコードを最初に通過しなければなりません。 Aの静的なブロックコードがBのコードの前に起こることを意味します、私は思います。おそらく –

2

extends ParentClassが存在しない場合でも、ParentClassを使用すると、初期化されるはずです。

物事が複雑になる場所は、サイクルがあるときです。サイクルでは、完全に初期化される前にクラスにアクセスすることが可能です。 Javaはマルチスレッドシステムなので、デッドロックや競合にも問題があります。

あなたのJava MEの実装がバグであるかもしれません。完全な部分ParentClassは、ChildClassを参照しています。あるいは、他のアプリケーション/ライブラリのバグがあるかもしれません。

関連するノートでは、-target 1.4以降を使用しないと、内部クラスのouterは期待通りに初期化されません。提示したように、あなたのコードは(静的なコンテキストで内部的なクラスを(技術的に)使用するので、問題ではありません。

静的な初期化は、実際には4つのクラスがあるため、このような状況では少し混乱していることを指摘してもおもしろいです。

0

、ParentClass.THE_SETが初期化されていないことを基本的に不可能です。

もちろん、1つのクラスローダーだけでなく、ヌルポインターが発生するメソッドと行番号を確認すると便利です。

+0

J2MEはカスタムクラスローダーを許可していません。それはむしろ原始的です。また、出力に 'f'がないことは、その前にあるunion()行でエラーが発生したことを意味します。 – izb

2

インスタンスの初期化(いわゆる "double brace idiom")のために、匿名クラスの概念を乱用することを単に止めてください。

+0

あなたはいつもこれが単なる便利な構文だと思っていたことは知っています。私はそれが実際には匿名のクラスであることは知らなかった。 – izb

+0

それは、私が「イディオム」と呼んで名前を付ける人々を嫌う最大の理由です。それは言語の別の特徴のように聞こえてしまい、実際に何が起こっているのかを隠してしまいます。 –

-1

Javaパズルの本やJavaのトリックについてのGoogleのYouTube動画にも同様のことが書かれていると思います。

関連する問題