Ich bin kürzlich auf ein Java-Konstrukt gestoßen, das ich noch nie gesehen habe, und habe mich gefragt, ob ich es verwenden soll. Es scheint Initialisierungsblöcke zu heißen .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
Der Codeblock wird in jeden Konstruktor kopiert, dh wenn Sie mehrere Konstruktoren haben, müssen Sie den Code nicht umschreiben.
Ich sehe jedoch drei Hauptnachteile bei der Verwendung dieser Syntax:
- Es ist einer der wenigen Fälle in Java, in denen die Reihenfolge Ihres Codes wichtig ist, da Sie mehrere Codeblöcke definieren können, die in der Reihenfolge ausgeführt werden, in der sie geschrieben wurden. Dies scheint mir schädlich zu sein, da eine einfache Änderung der Reihenfolge der Codeblöcke den Code tatsächlich ändert.
- Ich sehe keine wirklichen Vorteile, wenn ich es benutze. In den meisten Fällen rufen sich die Konstruktoren gegenseitig mit vordefinierten Werten auf. Auch wenn dies nicht der Fall ist, kann der Code einfach in eine private Methode gestellt und von jedem Konstruktor aufgerufen werden.
- Dies verringert die Lesbarkeit, da Sie den Block am Ende der Klasse einfügen können und der Konstruktor normalerweise am Anfang der Klasse steht. Es ist ziemlich kontraintuitiv, einen völlig anderen Teil einer Codedatei zu betrachten, wenn Sie nicht erwarten, dass dies erforderlich ist.
Wenn meine obigen Aussagen wahr sind, warum (und wann) wurde dieses Sprachkonstrukt eingeführt? Gibt es legitime Anwendungsfälle?
{ doStuff(); }
auf der Klassenebene ist ein Initialisierungsblock.doStuff()
Antworten:
Es gibt zwei Fälle, in denen ich Initialisierungsblöcke verwende.
Die erste dient zum Initialisieren der endgültigen Mitglieder. In Java können Sie ein endgültiges Element entweder direkt in der Deklaration oder im Konstruktor initialisieren. In einer Methode ist es verboten, ein endgültiges Mitglied zuzuweisen.
Das ist gültig:
Dies gilt auch:
Das ist ungültig:
Wenn Sie mehrere Konstruktoren haben und ein letztes Element nicht inline initialisieren können (weil die Initialisierungslogik zu komplex ist) oder wenn die Konstruktoren sich nicht selbst aufrufen können, können Sie den Initialisierungscode entweder kopieren / einfügen oder verwenden ein Initialisierungsblock.
Der andere Anwendungsfall, den ich für Initialisierungsblöcke habe, ist das Erstellen kleiner Hilfsdatenstrukturen. Ich deklariere ein Member und füge Werte direkt nach den Deklarationen in den eigenen Initialisierungsblock ein.
quelle
squareVal = val * val
beschweren sich daher über den Zugriff auf nicht initialisierte Werte. Initialisierungsblöcke können möglicherweise nicht von Argumenten abhängen, die an den Konstruktor übergeben werden. Die übliche Lösung für dieses Problem besteht darin, einen einzelnen "Basis" -Konstruktor mit der komplexen Logik zu definieren und alle anderen Konstruktoren in Bezug auf diesen zu definieren. Tatsächlich können die meisten Verwendungen von Instanzinitialisierern durch dieses Muster ersetzt werden.Verwenden Sie im Allgemeinen keine nicht statischen Initialisierungsblöcke (und vermeiden Sie möglicherweise auch statische).
Verwirrende Syntax
Wenn Sie sich diese Frage ansehen, gibt es 3 Antworten, aber Sie haben 4 Personen mit dieser Syntax getäuscht. Ich war einer von ihnen und schreibe seit 16 Jahren Java! Natürlich ist die Syntax möglicherweise fehleranfällig! Ich würde mich davon fernhalten.
Teleskopbauer
Für wirklich einfache Dinge können Sie "teleskopierende" Konstruktoren verwenden, um diese Verwirrung zu vermeiden:
Builder-Muster
Wenn Sie doStuff () am Ende eines jeden Konstruktors oder einer anderen raffinierten Initialisierung benötigen, ist möglicherweise ein Builder-Muster am besten geeignet. Josh Bloch nennt mehrere Gründe, warum Bauherren eine gute Idee sind. Bauherren nehmen sich etwas Zeit zum Schreiben, aber richtig geschrieben ist es eine Freude, sie zu benutzen.
Statische Initialisierungsschleifen
Früher habe ich häufig statische Initialisierer verwendet, aber gelegentlich kam es zu Schleifen, in denen zwei Klassen davon abhingen, ob die statischen Initialisiererblöcke des jeweils anderen aufgerufen wurden, bevor die Klasse vollständig geladen werden konnte. Dies erzeugte eine "Klasse konnte nicht geladen werden" oder eine ähnlich vage Fehlermeldung. Ich musste Dateien mit der letzten bekannten Arbeitsversion in der Quellcodeverwaltung vergleichen, um herauszufinden, wo das Problem lag. Gar kein Spaß.
Lazy Initialization
Vielleicht sind statische Initialisierer aus Performancegründen gut, wenn sie funktionieren und nicht zu verwirrend. Aber im Allgemeinen bevorzuge ich in diesen Tagen die verzögerte Initialisierung gegenüber statischen Initialisierern. Es ist klar, was sie tun, ich habe noch keinen Fehler beim Laden von Klassen mit ihnen entdeckt, und sie funktionieren in mehr Initialisierungssituationen als Initialisierungsblöcke.
Datendefinition
Anstelle der statischen Initialisierung zum Erstellen von Datenstrukturen (vergleiche die Beispiele in den anderen Antworten) verwende ich jetzt die unveränderlichen Hilfefunktionen für die Datendefinition von Paguro :
Erschütterung
Zu Beginn von Java waren Initialisierungsblöcke die einzige Möglichkeit, einige Dinge zu tun, aber jetzt sind sie verwirrend, fehleranfällig und wurden in den meisten Fällen durch bessere Alternativen ersetzt (siehe oben). Es ist interessant, Informationen zu Initialisierungsblöcken zu erhalten, falls Sie sie im Legacy-Code sehen oder wenn sie in einem Test auftauchen. Wenn ich jedoch eine Codeüberprüfung durchführe und einen neuen Code sehe, möchte ich Sie bitten, zu begründen, warum keiner der Blöcke vorhanden ist Die oben genannten Alternativen waren geeignet, bevor Sie Ihrem Code die Daumen hoch gaben.
quelle
Zusätzlich zur Initialisierung einer Instanzvariablen, die als deklariert ist
final
(siehe Antwort von Barjak ), würde ich auch denstatic
Initialisierungsblock erwähnen .Sie können sie als eine Art "statischer Konstruktor" verwenden.
Auf diese Weise können Sie komplexe Initialisierungen für eine statische Variable ausführen, wenn zum ersten Mal auf die Klasse verwiesen wird.
Hier ist ein Beispiel, das von Barjaks inspiriert wurde:
quelle
Was nicht statische Initialisierungsblöcke betrifft, besteht ihre reine Funktion darin, in anonymen Klassen als Standardkonstruktor zu fungieren. Das ist im Grunde ihr einziges Existenzrecht.
quelle
Ich stimme den Aussagen 1, 2 und 3 voll und ganz zu. Aus diesen Gründen verwende ich auch nie Blockinitialisierer, und ich weiß nicht, warum sie in Java existieren.
Ich bin jedoch gezwungen, den statischen Blockinitialisierer in einem Fall zu verwenden: Wenn ich ein statisches Feld instanziieren muss, dessen Konstruktor eine aktivierte Ausnahme auslösen kann.
Aber stattdessen müssen Sie Folgendes tun:
Ich finde dieses Idiom sehr hässlich (es verhindert auch das Markieren
context
alsfinal
), aber dies ist die einzige von Java unterstützte Möglichkeit, solche Felder zu initialisieren.quelle
context = null;
in Ihrem catch-Block festlegen , können Sie möglicherweise den Kontext als endgültig deklarieren.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }