Was bedeuten geschweifte Klammern in Java für sich?

79

Ich habe Java-Code, der geschweifte Klammern auf zwei Arten verwendet

// Curly braces attached to an 'if' statement:
if(node.getId() != null)
{
    node.getId().apply(this);
}

// Curly braces by themselves:
{
    List<PExp> copy = new ArrayList<PExp>(node.getArgs());
    for(PExp e : copy)
    {
        e.apply(this);
    }
}
outAMethodExp(node);

Was bedeuten diese eigenständigen geschweiften Klammern nach der ersten ifAussage?

Paul Wicks
quelle

Antworten:

120

Der einzige Zweck der zusätzlichen Klammern besteht darin, eine Bereichsbegrenzung bereitzustellen. Der List<PExp> copyWille existiert nur innerhalb dieser Klammern und hat keinen Umfang außerhalb von ihnen.

Wenn dies generierter Code ist, gehe ich davon aus, dass der Codegenerator dies tut, damit er Code (wie diesen) einfügen kann, ohne sich Gedanken darüber machen zu müssen, wie oft er einen Code eingefügt hat, List<PExp> copyund ohne sich Gedanken darüber machen zu müssen, ob die Variablen bei diesem Snippet möglicherweise umbenannt werden wird mehrmals in dieselbe Methode eingefügt.

matt b
quelle
9
Ein zusätzlicher "Vorteil" in diesem Fall ist, copydass Müll vor der outAMethodExp()Rückgabe gesammelt werden kann . Wenn dies ein lang andauernder oder speicherintensiver Anruf ist, kann dies hilfreich sein. Ich habe "Vorteil" in Anführungszeichen gesetzt, weil das Refactoring in separate Methoden im Allgemeinen viel sauberer und klarer ist, als diese Syntax zu nutzen.
dimo414
1
Eigentlich eine Variable Scoping hat nicht einen Effekt auf die Garbage Collection. In allen mir bekannten modernen JVMs kann die JIT feststellen, dass ein Objekt für die GC geeignet ist, unabhängig davon, ob Scoping-Konstrukte wie geschweifte Klammern vorhanden sind oder nicht.
Daniel Pryden
27

Ich stimme dem zu, was matt b geschrieben hat, und ich füge hinzu, dass eine andere Verwendung von anonymen Klammern darin besteht, einen impliziten Konstruktor in anonymen Klassen zu deklarieren. Zum Beispiel:

  List<String> names = new ArrayList<String>() {
    // I want to initialize this ArrayList instace in-line,
    // but I can't define a constructor for an anonymous class:
      {
        add("Adam");
        add("Eve");
      }

  };

Einige Unit-Testing-Frameworks haben diese Syntax auf eine andere Ebene gebracht, wodurch einige raffinierte Dinge, die völlig unkompilierbar aussehen, funktionieren. Da sie ungewohnt aussehen , bin ich selbst kein so großer Fan, aber es lohnt sich, zumindest zu erkennen, was los ist, wenn Sie auf diese Verwendung stoßen.

Dov Wasserman
quelle
Es tut mir leid, ich verstehe diesen Code nicht. Ist "Hinzufügen" einer Klasse oder einer Funktion. Wenn es eine Funktion ist: Zu welcher Klasse gehört sie? Akzeptiert ArrayList in diesem Fall einen Delegatentyp?
Peter Mortensen
7
"add" ist eine Funktion. Das Material in den geschweiften Klammern wird vor dem Konstruktor aufgerufen, um eine vorläufige Initialisierung durchzuführen. Weitere Informationen finden Sie unter c2.com/cgi/wiki?DoubleBraceInitialization .
Zaven Nahapetyan
1
Technisch gesehen wird der Konstruktor zuerst aufgerufen und der Instanzinitialisierungsblock wird unmittelbar nach dem Aufruf von aufgerufen super(...).
Oli
8

Ich stimme der Antwort auf die Bereichsbegrenzung zu, möchte aber eines hinzufügen.

Manchmal sieht man ein solches Konstrukt im Code von Personen, die gerne Teile ihres Codes falten und über Editoren verfügen, die Klammern automatisch falten. Sie verwenden es, um ihren Code in logischen Abschnitten zusammenzufassen, die nicht in eine Funktion, Klasse, Schleife usw. fallen, die normalerweise zusammengelegt werden.

Eli
quelle
4

Ich würde tatsächlich vermuten, dass jemand eine andere Aussage vergessen hat.

Es gibt selten einen guten Grund, sich die Mühe zu machen, zusätzliche Blockbereiche zu erstellen. In diesem und den meisten Fällen ist es weitaus wahrscheinlicher, dass jemand vergessen hat, seine Steueranweisung einzugeben, als dass er etwas Kluges getan hat.

Patrick
quelle
2
Ich stimme dich ab, nur weil es passieren kann, passiert ist und Occam in seinem Grab rollt, weil du abgewählt wurdest. :)
willasaywhat
1
Es wird von SableCC generiert. Ich wette 5 $, die sie sonst nicht vergessen haben
Pavel Feldman
Zusätzliche Klammern sind nützlich, um zu verhindern, dass globale Variablen in einer langen Methode auf Sie zukommen, bei der Sie einige ähnliche Codeblöcke an derselben Stelle aufbewahren möchten, an der die Blöcke nicht komplex genug sind, um neue Methoden zu rechtfertigen.
Jayunit100
Ich markiere Java-Kurse an einer Universität und kann sehen, dass dies tatsächlich oft der genaue Grund ist, warum es im Code gefunden wird. Wenn Sie Leute neu faktorisieren, schneiden Sie sie manchmal aus / fügen Sie sie ein und sehen Sie dann die falsche Anzahl von Klammern. Fügen Sie einfach eine hinzu. Aus diesem Grund stimme ich darüber ab. Nicht immer eine andere Aussage, aber oft nur ein einfacher Fehler.
ThePerson
Ich habe dich positiv bewertet, weil -1 Stimmen bedeutet, dass du nicht einmal hätte teilnehmen sollen. Vielleicht wusstest du es nicht, aber du hast dich bemüht. Es geht mir nicht um Trophäen für alle, aber "Danke" ist genau das, was ich Leuten sage, die versuchen zu helfen. (+1 == danke).
user426364
2

Sie bilden einen inneren Bereich. Die in diesen Klammern deklarierte Variable ist außerhalb nicht sichtbar. Dies gilt auch für C / C ++.

Paweł Hajdan
quelle
1

Klammern sind auch nützlich, um den Umfang von switch / case-Anweisungen zu verringern.

switch(foo) {
  case BAR:
     int i = ...
     ...
  case BAZ:
     int i = ... // error, "i" already defined in scope
}

Aber du kannst schreiben

switch(foo) {
  case BAR:{
     int i = ...
     ...
  }
  case BAZ:{
     int i = ... // OK
  }
}
Philipp
quelle
1

Es wird auch für Initialisierungsblöcke verwendet .

Gabriel
quelle
Erwähnenswert ist wahrscheinlich, dass dies nur für die statische Klasseninitialisierung außerhalb des Konstruktors gilt. Das Code-Snippet des OP befindet sich in einem Methodenblock (nur möglicher Ort dafür).
mmoore
Ein Initialisierungsblock ist für statische Klasseninitialisierung wenn es vorangestellt wird staticsonst sein ein Beispiel Initialisierungsblock.
Gabriel
Wahre Geschichten. Danke für die Klarstellung.
mmoore
0

Sie definieren einen neuen Bereich, was bedeutet, dass alles, was in diesem Bereich deklariert ist, außerhalb der geschweiften Klammern nicht sichtbar ist.

fhe
quelle
0

Als interessanter Hinweis: Die geschweiften Klammern ermöglichen tatsächlich eine Klasse von Anweisungen: Deklarationen.

Das ist illegal: if(a) int f;

aber das ist legal: if(a) { int f; }

Hugo
quelle
Genau dieser Code ist nutzlos, Sie können f außerhalb von geschweiften Klammern nicht sehen. Und um es zu verwenden, müssen Sie es im selben Bereich deklarieren und verwenden. Sie benötigen also mehr als eine Anweisung, also benötigen Sie geschweifte Klammern.
Pavel Feldman
Das ist der Punkt der Compiler-Überprüfung, den ich erwähne. Ich finde es interessant, dass es einen Unterschied zwischen dem "impliziten Umfang" (den ich für jeden Einzeiler ohne geschweifte Klammern halte) und den expliziten gibt. Es ist leicht zu vergessen, dass der Compiler einen Unterschied machen würde.
Hugo
Was ist mit if (userWantsTocInReport) {new Toc (report};}?) Ich sage nicht, dass es der ideale Weg ist, es zu schreiben, aber es ist eine Möglichkeit, die jemand wählen kann, aus welchem ​​Grund auch immer.
user625488
0

Ich denke, sie definieren nur eine unbenannte Ebene des Umfangs.

Peter Mortensen
quelle
0

Wenn Sie einen Bereich mitbringen, wird die Kopie außerhalb des Bereichs nicht sichtbar sein, sodass Sie später eine andere Variable mit demselben Namen deklarieren können. Und es kann vom Garbage Collector direkt nach dem Verlassen dieses Bereichs erfasst werden. In diesem Fall dient die Kopie als temporäre Variable, daher ist dies ein gutes Beispiel.

Pavel Feldman
quelle