Was ist die Syntax für die Initialisierung der doppelten Klammer ( {{ ... }}
) in Java?
Antworten
Zu viele Anzeigen?Die Initialisierung der doppelten Klammer erzeugt eine anonyme Klasse, die von der angegebenen Klasse abgeleitet ist (die Außen Klammern), und bietet einen Initialisierungsblock innerhalb dieser Klasse (die innen Klammern). z.B.
new ArrayList<Integer>() {{
add(1);
add(2);
}};
Beachten Sie, dass eine Auswirkung der Verwendung dieser doppelten Klammerinitialisierung ist, dass Sie anonyme innere Klassen erstellen. Die erstellte Klasse hat eine implizite this
Zeiger auf die umgebende äußere Klasse. Obwohl dies normalerweise kein Problem darstellt, kann es unter bestimmten Umständen, z. B. bei der Serialisierung oder beim Garbage Collecting, zu Problemen führen, und man sollte sich dessen bewusst sein.
Jedes Mal, wenn jemand eine doppelte Klammerinitialisierung verwendet, wird ein Kätzchen getötet.
Abgesehen davon, dass die Syntax eher ungewöhnlich und nicht wirklich idiomatisch ist (über Geschmack lässt sich natürlich streiten), schaffen Sie damit unnötigerweise zwei erhebliche Probleme in Ihrer Anwendung, über die ich erst kürzlich hier in einem Blog ausführlicher berichtet habe .
1. Sie erstellen viel zu viele anonyme Klassen
Jedes Mal, wenn Sie die doppelte Klammerinitialisierung verwenden, wird eine neue Klasse erstellt. Z.B. dieses Beispiel:
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
... wird diese Klassen produzieren:
Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class
Das ist ein ziemlicher Overhead für Ihren Classloader - für nichts! Natürlich kostet es nicht viel Zeit für die Initialisierung, wenn Sie es einmal tun. Aber wenn Sie dies 20.000 Mal in Ihrer Unternehmensanwendung tun... all der Heap-Speicher nur für ein bisschen "Syntaxzucker"?
2. Sie schaffen möglicherweise ein Speicherleck!
Wenn Sie den obigen Code nehmen und diese Map von einer Methode zurückgeben, könnten die Aufrufer dieser Methode ahnungslos an sehr schweren Ressourcen festhalten, die nicht entsorgt werden können. Betrachten Sie das folgende Beispiel:
public class ReallyHeavyObject {
// Just to illustrate...
private int[] tonsOfValues;
private Resource[] tonsOfResources;
// This method almost does nothing
public Map quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
return source;
}
}
Die zurückgegebenen Map
enthält nun einen Verweis auf die umschließende Instanz von ReallyHeavyObject
. Das wollen Sie wahrscheinlich nicht riskieren:
Bild aus <a href="http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/">http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/</a>
3. Sie können so tun, als hätte Java Map-Literale
Um Ihre eigentliche Frage zu beantworten: Die Leute haben diese Syntax verwendet, um so zu tun, als ob Java so etwas wie Map-Literale hätte, ähnlich wie die bestehenden Array-Literale:
String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};
Manche Menschen mögen dies syntaktisch anregend finden.
- Die erste Klammer erzeugt eine neue anonyme innere Klasse.
- Der zweite Satz von Klammern erzeugt einen Instanzinitialisierer wie den statischen Block in Class.
Zum Beispiel:
public class TestHashMap {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>(){
{
put("1", "ONE");
}{
put("2", "TWO");
}{
put("3", "THREE");
}
};
Set<String> keySet = map.keySet();
for (String string : keySet) {
System.out.println(string+" ->"+map.get(string));
}
}
}
Wie es funktioniert
Erste Klammer erstellt eine neue anonyme innere Klasse. Diese inneren Klassen sind in der Lage, auf das Verhalten ihrer übergeordneten Klasse zuzugreifen. In unserem Fall erstellen wir also eine Unterklasse der Klasse HashSet, so dass diese innere Klasse die Methode put() verwenden kann.
Und Zweiter Satz Hosenträger sind nichts anderes als Instanzinitialisierer. Wenn Sie sich an die Kernkonzepte von Java erinnern, können Sie Instanzinitialisierungsblöcke aufgrund der ähnlichen Klammer wie struct leicht mit statischen Initialisierungen in Verbindung bringen. Der einzige Unterschied besteht darin, dass statische Initialisierer mit dem Schlüsselwort static hinzugefügt werden und nur einmal ausgeführt werden, unabhängig davon, wie viele Objekte Sie erstellen.
Eine unterhaltsame Anwendung der Initialisierung von Doppelklammern finden Sie hier Dwemthy's Array in Java .
Ein Auszug
private static class IndustrialRaverMonkey
extends Creature.Base {{
life = 46;
strength = 35;
charisma = 91;
weapon = 2;
}}
private static class DwarvenAngel
extends Creature.Base {{
life = 540;
strength = 6;
charisma = 144;
weapon = 50;
}}
Und jetzt, seien Sie bereit für die BattleOfGrottoOfSausageSmells
und stückiger Speck!
Ich denke, es ist wichtig zu betonen, dass In Java gibt es keine "Double Brace Initialisierung". . Auf der Oracle-Website gibt es diesen Begriff nicht. In diesem Beispiel werden zwei Funktionen zusammen verwendet: anonyme Klasse und Initialisierungsblock. Es scheint so, als ob der alte Initialisierungsblock von den Entwicklern vergessen wurde und für einige Verwirrung in diesem Bereich sorgt. Zitat aus Oracle-Dokumente :
Initialisierungsblöcke für Instanzvariablen sehen genauso aus wie statische Initialisierungsblöcke, nur ohne das Schlüsselwort static:
{
// whatever code is needed for initialization goes here
}
- See previous answers
- Weitere Antworten anzeigen