Java中的匿名代码块

Rob*_*anu 62 java

Java中是否存在匿名代码块的实际用途?

public static void main(String[] args) {
    // in
    {
        // out
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这不是关于命名块,即

name: { 
     if ( /* something */ ) 
         break name;
}
Run Code Online (Sandbox Code Playgroud)

.

Dav*_*ler 123

它们限制了变量范围.

public void foo()
{
    {
        int i = 10;
    }
    System.out.println(i); // Won't compile.
}
Run Code Online (Sandbox Code Playgroud)

但实际上,如果你发现自己使用了这样一个代码块,那可能就是你想要将那个块重构为一个方法的迹象.

  • +1:正确的答案,这是一个迫切需要的重构的标志,也就是Martin Fowler所说的"代码味道":) (5认同)
  • 很好的答案,完全同意方法评论. (3认同)
  • 您不希望将其分解为私有方法的唯一时间是该代码块需要大量上下文(以其他局部变量的形式).但这可能是另一种代码味道...... (3认同)

Ste*_*sen 43

@David Seiler的答案是正确的,但我认为代码块非常有用,应该经常使用,并不一定表明需要考虑到方法.我发现它们对于构造Swing组件树特别有用,例如:

JPanel mainPanel = new JPanel(new BorderLayout());
{
    JLabel centerLabel = new JLabel();
    centerLabel.setText("Hello World");
    mainPanel.add(centerLabel, BorderLayout.CENTER);
}
{
    JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0,0));
    {
        JLabel label1 = new JLabel();
        label1.setText("Hello");
        southPanel.add(label1);
    }
    {
        JLabel label2 = new JLabel();
        label2.setText("World");
        southPanel.add(label2);
    }
    mainPanel.add(southPanel, BorderLayout.SOUTH);
}
Run Code Online (Sandbox Code Playgroud)

代码块不仅限制了变量的范围(这总是好的,尤其是在处理可变状态和非最终变量时),但它们也像XML/HTML一样说明了组件层次结构的方式.代码更易于阅读,编写和维护.

我将每个组件实例化分解为方法的问题是

  1. 即使它是私有实例方法,该方法也只会使用一次但暴露给更广泛的受众.
  2. 阅读起来比较困难,想象一个更深层次更复杂的组件树,你必须向下钻取以找到你感兴趣的代码,然后放松视觉上下文.

在这个Swing示例中,我发现当复杂性确实超出可管理性时,它表明是时候将树的一个分支分解为一个新类而不是一堆小方法.

  • 我只是在处理使用这个"功能"的代码,而且很难遵循.我认为这是构建代码的一种糟糕方式 (9认同)
  • @Jan"相当难以理解"和"难以理解的混乱"是主观意见.他们所总结的是"我不习惯这样做,因此一定是坏事." 斯蒂芬解释了为什么这个更好的目标.(而且我恰好同意.)所以,要么提出_objective_反对意见,要么保持沉默. (8认同)
  • 在我看来,这种编码风格很方便,代码已经*不可理解地混乱,难以理解.它只会让*更容易*跟随.我认为一个类最好包含50个匿名代码黑色而不是50个小的一次性函数.如果有的话,我想听听不同意见. (3认同)
  • @Jan - 有很多方法可以很好地使用好的功能,我可以想象这个很容易失控(深度嵌套,长滚动,大量突变......). (2认同)
  • 我更喜欢:`mainPanel.add(label("Hello World",...);`和`southPanel.add(label("Hello"));`和`southPanel.add(label("World") ")没有额外的大括号.我无法理解它是如何难以阅读的,因为适当命名的方法应该提供关于其意图的线索.此外,IDE通过将鼠标悬停在方法上来显示Javadocs作为工具提示,这意味着视觉上下文不是丢失.重构通常会降低(或引起注意)复制/粘贴错误的可能性. (2认同)

Kev*_*n K 23

通常最好使局部变量的范围尽可能小.匿名代码块可以帮助解决这个问题.

我发现这对switch语句特别有用.考虑以下示例,没有匿名代码块:

public String manipulate(Mode mode) {
    switch(mode) {
    case FOO: 
        String result = foo();
        tweak(result);
        return result;
    case BAR: 
        String result = bar();  // Compiler error
        twiddle(result);
        return result;
    case BAZ: 
        String rsult = bar();   // Whoops, typo!
        twang(result);  // No compiler error
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用匿名代码块:

public String manipulate(Mode mode) {
    switch(mode) {
        case FOO: {
            String result = foo();
            tweak(result);
            return result;
        }
        case BAR: {
            String result = bar();  // No compiler error
            twiddle(result);
            return result;
        }
        case BAZ: {
            String rsult = bar();   // Whoops, typo!
            twang(result);  // Compiler error
            return result;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为第二个版本更清晰,更容易阅读.并且,它减少了在切换到声明它们的情况下声明的变量的范围,根据我的经验,这是你想要99%的时间.

但要注意,它并没有改变案件掉落的行为 - 你仍然需要记住包括breakreturn防止它!


Ste*_*n C 17

我认为你和/或其他答案混淆了两种不同的句法结构; 即实例初始化器和块.(顺便说一句,"命名块"实际上是一个标记语句,其中语句恰好是一个块.)

Instance Initializer用于类成员的语法级别; 例如

public class Test {
    final int foo;

    {
         // Some complicated initialization sequence; e.g.
         int tmp;
         if (...) {
             ...
             tmp = ...
         } else {
             ...
             tmp = ...
         }
         foo = tmp;
    }
}
Run Code Online (Sandbox Code Playgroud)

根据@dfa的例子,Initializer构造最常用于匿名类.另一个用例是对'final'属性进行复杂的初始化; 例如,参见上面的例子.(但是,使用常规构造函数更常见.上面的模式更常用于静态初始化器.)

另一个构造是一个普通的块,并出现在代码块中,例如方法; 例如

public void test() {
    int i = 1;
    {
       int j = 2;
       ...
    }
    {
       int j = 3;
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

块最常用作控制语句的一部分,用于对一系列语句进行分组.但是当你在上面使用它们时,它们(只是)允许你限制声明的可见性; 例如j在上面.

这通常表明您需要重构代码,但并不总是很明确.例如,您有时会在使用Java编码的解释器中看到这种情况.交换机臂中的语句可以分解为单独的方法,但这可能会导致解释器的"内环"出现重大性能损失; 例如

    switch (op) {
    case OP1: {
             int tmp = ...;
             // do something
             break;
         }
    case OP2: {
             int tmp = ...;
             // do something else
             break;
         }
    ...
    };
Run Code Online (Sandbox Code Playgroud)


Osc*_*Ryz 12

您可以将它用作匿名内部类的构造函数.

像这样:

替代文字http://img113.imageshack.us/img113/888/capturadepantalla200910.png

这样就可以初始化对象,因为在对象构造期间执行了空闲块.

它不仅限于匿名内部类,它也适用于常规类.

public class SomeClass {
    public List data;{
        data = new ArrayList();
        data.add(1);
        data.add(1);
        data.add(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这实际上是一个实例初始化器,这是另一回事. (6认同)
  • 与...不同 建设者?哦,是的,当指代构造函数时,我的意思是“作为..的替代或补充。” (2认同)

dfa*_*dfa 7

匿名块对于限制变量的范围以及双括号初始化非常有用.

相比

Set<String> validCodes = new HashSet<String>();
validCodes.add("XZ13s");
validCodes.add("AB21/X");
validCodes.add("YYLEX");
validCodes.add("AR2D");
Run Code Online (Sandbox Code Playgroud)

Set<String> validCodes = new HashSet<String>() {{
  add("XZ13s");
  add("AB21/X");
  add("YYLEX");
  add("AR5E");
}};
Run Code Online (Sandbox Code Playgroud)

  • 我知道双支撑初始化技巧,但它不甜,这是一个肮脏的把戏,会让那些不知道它的人感到困惑.不要用它! (6认同)
  • Bevare尽管在第二个实例中,validCodes现在不是HashSet,而是HashSet的子类.对于更复杂的类,这可能会导致一些意外行为. (5认同)
  • 同样,实例初始化程序与块不同. (4认同)
  • +1,但最后一个括号是多余的. (2认同)