¿Qué significan las llaves en Java por sí mismas?

79

Tengo un código Java que usa llaves de dos maneras

// 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);

¿Qué significan esas llaves independientes después de la primera ifdeclaración?

Paul Wicks
fuente

Respuestas:

120

El único propósito de las llaves adicionales es proporcionar un límite de alcance. El List<PExp> copysolo existirá dentro de esos soportes y no tendrá alcance fuera de ellos.

Si se trata de un código generado, supongo que el generador de código hace esto para que pueda insertar algún código (como este) sin tener que preocuparse por cuántas veces ha insertado un List<PExp> copyy sin tener que preocuparse por la posibilidad de cambiar el nombre de las variables si este fragmento se inserta en el mismo método más de una vez.

mate b
fuente
9
Un "beneficio" adicional en este caso es que copyse puede recolectar la basura antes de las outAMethodExp()devoluciones. Si se trata de una llamada de larga duración o que requiere mucha memoria, puede resultar útil. Puse "beneficio" entre comillas porque la refactorización en métodos separados es generalmente mucho más limpia y clara que aprovechar esta sintaxis.
dimo414
1
En realidad, el ámbito de una variable no tiene ningún efecto sobre la recolección de basura. En todas las JVM modernas que conozco, el JIT puede determinar que un objeto es elegible para GC independientemente de la presencia o ausencia de construcciones de alcance como llaves.
Daniel Pryden
27

Secundo lo que escribió Matt b, y agregaré que otro uso que he visto de llaves anónimas es declarar un constructor implícito en clases anónimas. Por ejemplo:

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

  };

Algunos marcos de pruebas unitarias han llevado esta sintaxis a otro nivel, lo que permite que funcionen algunas cosas ingeniosas que parecen totalmente incompilables. Dado que no parecen familiares, yo mismo no soy un gran admirador, pero vale la pena reconocer al menos lo que está sucediendo si se encuentra con este uso.

Dov Wasserman
fuente
Lo siento, no entiendo este código. Es "agregar" una clase o una función. Si es una función: ¿a qué clase pertenece? ¿ArrayList acepta un tipo de delegado en este caso?
Peter Mortensen
7
"agregar" es una función. Las cosas dentro de las llaves se llaman antes que el constructor para realizar una inicialización preliminar. Puede consultar c2.com/cgi/wiki?DoubleBraceInitialization para obtener más información.
Zaven Nahapetyan
1
Técnicamente, el constructor se llama primero y el bloque de inicialización de la instancia se llama inmediatamente después de la llamada a super(...).
Oli
8

Estoy de acuerdo con la respuesta del límite de alcance, pero agregaría una cosa.

A veces ves una construcción como esa en el código de personas a las que les gusta doblar secciones de su código y tienen editores que doblan llaves automáticamente. Lo usan para plegar su código en secciones lógicas que no caen en una función, clase, bucle, etc. que normalmente se plegaría.

Eli
fuente
4

De hecho, supongo que alguien olvidó una declaración else.

Rara vez hay una buena razón para molestarse en crear ámbitos de bloque adicionales. En este, y en la mayoría de los casos, es mucho más probable que alguien haya olvidado escribir su declaración de control que que estaba haciendo algo inteligente.

Patricio
fuente
2
Te voy a votar simplemente porque puede suceder, ha sucedido, y Occam está rodando en su tumba porque te votaron en contra. :)
willasaywhat
1
Es generado por SableCC. Apuesto 5 $ que no se olvidaron de nada más
Pavel Feldman
Las llaves adicionales son útiles para evitar que las variables globales se acerquen a usted en un método largo en el que desea mantener algunos bloques de código similares en el mismo lugar, donde los bloques no son lo suficientemente complejos como para justificar nuevos métodos.
jayunit100
Marco el trabajo de curso de Java en una universidad y puedo ver que esta es a menudo la razón exacta por la que se encuentra en el código. Al volver a factorizar, las personas a veces cortan / pegan y luego ven el número incorrecto de llaves, ... solo agregue uno ... luego mire con qué termina. Por esa razón estoy votando esto. No siempre es una declaración else, pero a menudo es un simple error.
ThePerson
Te voté porque -1 votos significa que ni siquiera deberías haber participado. Quizás no lo sabías, pero te esforzaste. No se trata de trofeos para todos, pero "gracias" es solo lo que les digo a las personas que intentan ayudar. (+1 == gracias).
user426364
2

Hacen un alcance interior. La variable declarada dentro de estas llaves no es visible fuera de ellas. Esto también se aplica a C / C ++.

Paweł Hajdan
fuente
1

Las llaves también son útiles para reducir el alcance en declaraciones switch / case.

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

Pero puedes escribir

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

También se utiliza para bloques de inicialización .

Gabriel
fuente
Probablemente valga la pena mencionar que esto es solo para la inicialización de clases estáticas fuera del constructor. El fragmento de código de OP está en un bloque de método (el único lugar posible para él).
mmoore
Un bloque de inicialización es para la inicialización de clases estáticas si está precedido por, de lo staticcontrario, es un bloque de inicialización de instancia .
Gabriel
Cuentos verdaderos. Gracias por la aclaración.
mmoore
0

Definen un nuevo alcance, lo que significa que todo lo declarado en este alcance no es visible fuera de las llaves.

el
fuente
0

Como nota interesante: las llaves en realidad permiten una clase de declaraciones: declaraciones.

Esto es ilegal: if(a) int f;

pero esto es legal: if(a) { int f; }

Hugo
fuente
Exactamente este código es inútil, no podrá ver f fuera de las llaves. Y para usarlo, debe declararlo y usarlo dentro del mismo alcance. Entonces necesita más de una declaración, por lo que necesita aparatos ortopédicos.
Pavel Feldman
ese es el punto de la verificación del compilador que estoy mencionando. Me parece interesante que haya una diferencia en el "alcance implícito" (que considero que es cada una de las líneas sin llaves) y los explícitos. Es fácil olvidar que el compilador marcaría la diferencia.
Hugo
¿Qué pasa con if (userWantsTocInReport) {new Toc (report};}? No estoy diciendo que sea la forma ideal de escribirlo, pero es una posibilidad que alguien pueda elegir, por el motivo que sea.
user625488
0

Creo que simplemente definen un nivel de alcance sin nombre.

Peter Mortensen
fuente
0

La copia de traer un alcance no será visible fuera de ella, por lo que puede declarar otra variable con el mismo nombre más adelante. Y el recolector de basura puede recopilarlo justo después de salir de ese alcance. En este caso, copy sirve como variable temporal, por lo que es un buen ejemplo.

Pavel Feldman
fuente