Java中的花括号自身代表什么?

85

我有一些使用花括号的Java代码,其用法有两种。

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

第一个if语句后面的那些独立的花括号是什么意思?

11个回答

126
额外的大括号的唯一目的是提供范围限制。在这些大括号内部,List<PExp> copy 只存在于它们之内,并且在它们之外没有作用域。
如果这是生成的代码,我假设代码生成器这样做是为了插入一些代码(例如这个)而不必担心插入了多少次 List<PExp> copy,也不必担心如果将此片段插入同一个方法中超过一次时可能会重命名变量。

11
在这种情况下,copy 可以在 outAMethodExp() 返回之前进行垃圾回收,这是一个额外的“好处”。如果这是一个长时间运行或内存密集型的调用,那可能会有所帮助。我用引号括起来的“好处”,是因为将代码重构为单独的方法通常比利用这种语法更清晰明了。 - dimo414
1
实际上,变量的作用域对垃圾回收没有影响。在我所知道的所有现代JVM中,即使没有花括号等作用域构造,JIT也可以确定对象是否符合GC的条件。 - Daniel Pryden

27

我赞同matt b所写的内容,并且我想补充一点关于匿名花括号的另一个用途,就是在匿名类中声明隐式构造函数,例如:

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

  };

一些单元测试框架已将此语法提升到另一个层次,这使得一些看起来完全无法编译的漂亮事情得以实现。由于它们看起来不太熟悉,我个人并不是很喜欢,但如果你遇到这种用法,至少要认识到其中的奥妙。请注意保留HTML标签。

对不起,我不理解这段代码。 "add" 是一个类还是一个函数?如果它是一个函数:它属于哪个类?在这种情况下,ArrayList 是否接受委托类型? - Peter Mortensen
7
"add"是一个函数。花括号中的内容在构造函数之前被调用,以执行一些预备初始化工作。您可以查看http://www.c2.com/cgi/wiki?DoubleBraceInitialization获取更多信息。 - Zaven Nahapetyan
1
技术上讲,构造器会首先被调用,随后在对super(...)的调用之后立即调用实例初始化块。 - Oli

9
我同意范围限制的答案,但是还想补充一点。有时你会在代码中看到这样的结构,这些人喜欢将他们的代码分成逻辑部分并使用编辑器自动折叠大括号来折叠代码。他们使用它来折叠不属于函数、类、循环等通常被折叠的部分的逻辑部分。

5

我猜测有人忘记了else语句。

创建额外的块作用域很少有好处。在这种情况下,以及大多数情况下,更有可能是某人忘记键入控制语句,而不是他们正在做一些聪明的事情。


3
我会投票支持你,仅仅因为它可能发生,已经发生了,并且Occam正在墓中翻转,因为你被投票否决了。 :) - willasaywhat
1
它是由SableCC生成的。我打赌5美元他们没有忘记else。 - Pavel Feldman
我在一所大学批改Java课程作业,可以看到这实际上经常是代码出现问题的确切原因。当重构代码时,人们有时会剪切/粘贴,然后发现括号数目不对了,只需添加一个.. 然后看看你得到了什么。因此我支持这个建议。问题不总是出在else语句上,而常常只是一个简单的错误。 - ThePerson
我给你点赞是因为-1票意味着你甚至不应该参与。也许你不知道,但你付出了努力。我不是为每个人颁发奖杯的人,但“谢谢”就是我对那些试图帮助我的人说的话。(+1 == 谢谢)。 - user426364
我从事编程工作已经有近18年了。我从未见过有人忘记控制语句 - 即使是在学生作业中也没有。"很少有充分的理由..."是完全误导的 - 有时候,限定范围比重构为单独的方法更可读。而且,你可能想要对某些本地变量进行限定,而对其他变量不进行限定。如果不进行限定,你最终会得到"有创意"的命名,比如 tmpList1、tmpList2 等等。 - user625488
显示剩余2条评论

2

它们产生了一个内部作用域。在这些大括号内声明的变量在外部不可见。C/C++也适用此规则。


1

花括号在switch/case语句中也很有用,可以缩小作用域。

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

但你可以写

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

1

可能值得一提的是,这仅适用于构造函数外的静态类初始化。 OPs 代码片段位于方法块中(唯一可能的位置)。 - Martin
如果初始化块以 static 为前缀,则用于静态类初始化,否则它是一个实例初始化块。 - Gabriel

0

我认为他们只是定义了一个未命名的作用域级别。


0

它们定义了一个新的作用域,意味着在这个作用域中声明的所有内容在花括号外是不可见的。


0

使用一个作用域,copy 变量就不会在该作用域外可见,因此您可以稍后声明另一个同名变量。并且在退出该作用域后,它可以被垃圾回收器清除。在这种情况下,copy 作为临时变量,是一个很好的例子。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接