5

Ich habe eine Set-Klasse (Dies ist J2ME, also habe ich begrenzten Zugriff auf die Standard-API; nur um meine scheinbare Rad-Neuerfindung zu erklären). Ich verwende meine Set-Klasse, um konstante Mengen von Dingen in Klassen und Unterklassen zu erstellen. Es sieht ein bisschen wie dieses ...Kann ich die Reihenfolge garantieren, in der statische Initialisierer in Java ausgeführt werden?

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] */ 
    }}; 
} 

Alle sieht gut aus, mit Ausnahme der Linie bei [1], um eine Null-Zeiger-Ausnahme verursacht. Vermutlich bedeutet dies, dass der statische Initialisierer in der Unterklasse vor dem der übergeordneten Klasse ausgeführt wird. Das hat mich überrascht, weil ich gedacht hätte, dass es die statischen Blöcke in jedem neuen Import zuerst ausführen würde, bevor es in der instatiated Subklasse ausgeführt wird.

Bin ich richtig in dieser Annahme? Gibt es eine Möglichkeit, dieses Verhalten zu kontrollieren oder zu umgehen?

Update:

Die Dinge sind noch seltsamer. Ich habe versucht, diese stattdessen (Man beachte die ‚neue Parent()‘ Zeile):

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

Und der Ausgang ist seltsam:

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

So Parent wird initialisiert, aber die Unterklasse haben keinen Zugriff auf es in seinem statischen Initialisierer.

Antwort

7

Ist es das, was Sie erreichen möchten? Oder benötigen Sie eine lokale Implementierung der Set-Schnittstelle?

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

Ja ... das macht das Gleiche. – izb

+1

Ich denke, Elijah hat es richtig gemacht. Dies ist kein Problem der Initialisierungsreihenfolge, sondern eher eine Art Namenskonflikt. – boutta

+0

In meinem echten Code habe ich tatsächlich 3 Klassen, wobei A <-B <-C ist. Die Ausnahme war in B. Interessant, jeder Klasse einen eigenen Gruppennamen zu geben, verschiebt die Ausnahme nach C, löst das Problem jedoch nicht. Das bedeutet einfach, dass die Reihenfolge wirklich unvorhersehbar ist. – izb

3

Es gibt keine Garantie für die Reihenfolge der statischen Initialisierer zwischen den Klassen. Innerhalb einer Klasse werden sie in der Reihenfolge des Quellcodes ausgeführt.

Wenn Sie darüber nachdenken, könnte therre wirklich keine Klassen sein, weil Sie nicht steuern, wenn die Klassen entweder geladen werden; Sie könnten eine Klasse dynamisch laden, oder die JVM könnte die Ladereihenfolge optimieren.

+0

Wenn Klasse A Klasse B erweitert, müsste Klasse A nicht vollständig geladen werden, bevor Klasse B geladen werden kann? –

+0

Ich hätte gedacht, es wäre anders herum. Sicherlich hängt A davon ab, dass B da ist, also sollte B zuerst initialisiert werden. – izb

+0

Aber um (oder wissen über B) zu bekommen, muss es zuerst durch den Code in A gehen. Das heißt, der A-Blockcode wird vor dem Code in B passieren, würde ich denken. –

2

Auch wenn Sie die extends ParentClass nicht dort hatten, sollte die Verwendung von ParentClass dazu führen, dass sie initialisiert wird.

Wo es schwierig wird, ist, wenn Sie Zyklen haben. Mit einem Zyklus ist es möglich, auf eine Klasse zuzugreifen, bevor sie vollständig initialisiert wurde. Da Java ein Multithread-System ist, können Sie auch Probleme mit Deadlocks und Rassen haben.

Möglicherweise ist Ihre Java ME-Implementierung fehlerhaft (nicht unbekannt). Teilen Sie Ihre volle Referenzen Ihre ChildClass. Oder vielleicht gibt es einen anderen Programm-/Bibliotheksfehler.

In einer verwandten Anmerkung, wenn Sie nicht -target 1.4 oder später, innere Klassen äußere dies ist nicht initialisiert, sobald Sie erwarten können. Wie dargestellt, verwendet Ihr Code (technisch) innere Klassen in einem statischen Kontext, so dass dies kein Problem sein sollte.

Es ist auch interessant, darauf hinzuweisen, dass die statische Initialisierung in Situationen wie dieser etwas verwirrt ist, weil Sie dort tatsächlich vier Klassen haben.

0

die Ausgangsleitung von [ „eins“, „zwei“, „drei“] Da ist es grundsätzlich unmöglich, dass ParentClass.THE_SET initialisiert wurde nie.

Natürlich ist es möglich, dass nicht nur ein Classloader beteiligt ist, aber es wäre sicherlich hilfreich, die Methode und die Zeilennummer zu sehen, wo der Nullzeiger passiert.

+0

J2ME erlaubt keine benutzerdefinierten Classloader .. es ist eher primitiv. Das Fehlen von "f" in der Ausgabe bedeutet außerdem, dass der Fehler in der ihm vorausgehenden Zeile union() aufgetreten ist. – izb

2

Hören Sie einfach auf, das Konzept der anonymen Klassen für die Initialisierung von Instanzen zu missbrauchen (das sogenannte "double brace idiom").

+0

Sie wissen, dass ich immer dachte, dies sei nur handliche Syntax .. Ich hatte keine Ahnung, es war eigentlich eine anonyme Klasse. – izb

+0

Das ist der Hauptgrund, warum ich es hasse, dass Leute es als "Idiom" bezeichnen und ihm einen Namen geben - es klingt wie ein separates Merkmal der Sprache und verstellt, was tatsächlich passiert. –

-1

Ich denke, etwas ähnliches wurde in Java-Puzzles Buch oder googles YouTube-Video über Java-Tricks geschrieben.