2013-01-18 9 views
15

Ich weiß, dass dieser Code:Java Double Brace Initialisierung funktioniert immer?

Set<String> set = new HashSet<String>() {{ 
    add("test1"); 
    add("test2"); 
}}; 

ist wirklich:

Set<String> set = new HashSet<String>() { 
    {//initializer 
    add("test1"); 
    add("test2"); 
    } 
}; 

Der Initialisierungsblocks vor der Konstruktor Block ausgeführt wird, ist. Im obigen Beispiel wird add ("test1") vor der Ausführung des Konstruktors aufgerufen. Der Konstruktor initialisiert möglicherweise viele der Instanzfelder, so dass diese Klasse funktionieren würde. Ich frage mich, warum Aufruf .add() vor dem Konstruktor würde funktionieren? Gibt es einen Fall, der ein Problem verursacht?

+2

Dies wird wahrscheinlich unter "nicht spezifiziertes Verhalten" fallen ... – 11684

+2

Interessante Frage. Ich habe keine wirkliche Antwort, aber ich denke, Sie machen hier eine falsche Annahme. Wenn man sich den Konstruktor von HashSet anschaut, tut man folgendes: map = new HashMap (); 'und die add-Methode macht das:' return map.put (e, PRESENT) == null; '. Wenn Ihre Annahme richtig war, würde dies eine NPE verursachen. –

+0

[Es ist sicherlich nicht falsch darüber nachzudenken, ob dieses "Muster" wirklich das Problem wert ist] (http://stackoverflow.com/q/924285/521799) –

Antwort

17

Es gibt ein Detail, das Sie weggelassen haben, das erklärt dies.

Zu allererst Bewertung lasst uns die Schritte 3 bis 5 der initialization procedure (zusammengefasst):

3. der Oberklassenkonstruktors genannt wird
4. die Instanz initializers sind
5. der Körper des Konstruktors heißt

Das Detail, das Sie weggelassen haben, ist, dass Ihr Ausdruck nicht einfach eine neue Instanz der Klasse HashSet erstellt, sondern tatsächlich eine neue Instanz einer anonymen Unterklasse von HashSet erstellt. (Ich glaube, das ist in section 15.9.1 angegeben.)

Da Sie keinen Konstruktor deklariert haben, wird der Standardkonstruktor verwendet. Aber vorher ist der Konstruktor der Oberklasse HashSet fertiggestellt.

Zusammenfassend wird also der HashSet-Konstruktor abgeschlossen, bevor der Initialisierungsblock ausgeführt wird.

+0

Danke, Samuel. Du hast das fehlende Teil genau erklärt. Dieser Codeblock initialisiert HashSet nicht, sondern eine anonyme Unterklasse von HashSet. – user926958

+0

@ user926958, es ist ein einfaches Detail in diesem Beispiel zu verpassen. Wenn Sie von einer Schnittstelle wie 'Set' oder einer abstrakten Klasse abgeleitet wären, müssten Sie einige Methodendefinitionen hinzufügen, die es offensichtlicher machen, dass hier eine neue Klasse definiert wird. –

6

Diese Annahme ist falsch:

Der Initialisierungsblocks vor dem Konstruktor Block ausgeführt wird.

Da in diesem speziellen Fall der Initialisierer Block Teil der Konstruktor Block ist.

Der docs Zustand deutlich, dass

Die Java-Compiler Kopien initializer Blöcke in jedem Konstruktor. Daher kann dieser Ansatz verwendet werden, um einen Codeblock zwischen mehreren Konstruktoren zu teilen.

Ich denke, Sie sind mit statischen Initialisierer verwechseln.

+0

Überprüfen Sie meinen Kommentar zu der ursprünglichen Frage. Müsste der Block nicht nach jedem Konstruktor statt vor diesem auftreten? Nicht dass du etwas anderes sagst, ich will es einfach besser verstehen. –

+0

Die Reihenfolge sieht aus, Initialisierer vor Konstruktor zu sein, zumindest habe ich es von hier: http://StackOverflow.com/Questions/2007666/In-What-Order-Do-Static-Initializer-Blocks-in-JavaRun – user926958

+1

Ich will nicht unhöflich sein, aber das ist NICHT, was passiert; der Initialisierungsblock wird nach Super aber vor dem Rest des Konstruktors aufgerufen, was in den meisten Fällen ein Problem verursachen würde; Er macht das in einer Inline-Erweiterung der HashMap-Klasse, weshalb das immer funktioniert. –

4

Instanzinitialisierer werden ausgeführt, unmittelbar nachdem das Objekt erstellt wurde. Sie erstellen im Grunde eine Inline-Erweiterung des HashSets und dann "direkt danach" werden zwei Elemente hinzugefügt.

Dies ist ein allgemeines Verwendungsmuster in Mock-Objekten zum Testen, wie in JMock, hat aber auch andere praktische Anwendungen.

Hoffe, das hilft.

+2

Es wird nicht ausgeführt, nachdem das Objekt erstellt wurde. Es wird während der Objektkonstruktion zwischen dem Superklassenkonstruktor und dem Konstruktorhauptteil ausgeführt. –

3

Ich halte dies für eine schlechte Praxis, weil es sinnlose Unterklassen erstellt, die die Speicherauslastung und die Leistung der Anwendung beeinflussen können. Wie auch immer, das Programm ist korrekt, weil der Superklassenkonstruktor vor den Instanzinitialisierern aufgerufen wird. Wenn der Initialisierer ausgeführt wird, wurde der HashSet-Konstruktor ausgeführt, sodass der Aufruf an add funktioniert.