Java中的匿名代码块
Java中有匿名代码块的实际用法吗?
public static void main(String[] args) { // in { // out } }
请注意,这不是关于命名块,即
name: { if ( /* something */ ) break name; }
。
他们限制variables的范围。
public void foo() { { int i = 10; } System.out.println(i); // Won't compile. }
但是在实践中,如果你发现自己使用这样的代码块,这可能是你想要将该块重构为方法的标志。
@David Seiler的答案是正确的,但我认为代码块是非常有用的,应该经常使用,并不一定表明需要将方法分解出来。 我发现它们对构buildSwing组件树尤其有用,例如:
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); }
不仅代码块尽可能紧密地限制variables的范围(这总是很好的,特别是在处理可变状态和非最终variables时),而且它们还以XML / HTML制作的方式说明组件层次结构代码更易于阅读,编写和维护。
将每个组件实例化为一个方法的问题就是我的问题
- 这个方法只会被使用一次,而且会暴露给更多的用户,即使它是一个私有的实例方法。
- 阅读起来比较困难,想象一个更深层次的更复杂的组件树,你不得不深入查找你感兴趣的代码,然后放宽可视上下文。
在这个Swing的例子中,我发现当复杂性确实超越可pipe理性时,它表明是时候把树的一个分支分解成一个新的类,而不是一堆小的方法。
通常最好使局部variables的范围尽可能小 。 匿名代码块可以帮助解决这个问题。
我发现这对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; } }
和匿名代码块:
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; } } }
我认为第二个版本更清晰,更易于阅读。 而且,它将交换机中声明的variables的范围缩小到所声明的情况,根据我的经验,99%的时间是你想要的。
不过要警惕的是,这并不会改变案件发生的行为 – 你仍然需要记住包括break
或return
以防止它!
我认为你和/或其他答案混淆了两个不同的句法结构; 即实例初始化程序和块。 (顺便说一句,“命名块”实际上是一个标签语句,其中语句恰好是一个块。)
实例初始化器用于类成员的语法级别; 例如
public class Test { final int foo; { // Some complicated initialization sequence; eg int tmp; if (...) { ... tmp = ... } else { ... tmp = ... } foo = tmp; } }
按照@ dfa的例子,Initializer构造最常用于匿名类。 另一个用例是对“final”属性进行复杂的初始化; 例如见上面的例子。 (然而,使用常规的构造函数更常见,上面的模式更常用于静态初始化函数。)
另一个构造是一个普通的块,并出现在一个代码块,如方法; 例如
public void test() { int i = 1; { int j = 2; ... } { int j = 3; ... } }
块最常用作控制语句的一部分来对一系列语句进行分组。 但是当你使用它们时,它们(只)允许你限制声明的可见性。 例如上面的j
。
这通常表示您需要重构代码,但并不总是清楚。 例如,你有时会在用Java编码的解释器中看到这样的事情。 交换机组中的陈述可以被分解成单独的方法,但是这可能导致解释器的“内部循环”显着的性能损失; 例如
switch (op) { case OP1: { int tmp = ...; // do something break; } case OP2: { int tmp = ...; // do something else break; } ... };
您可以将其用作匿名内部类的构造函数。
喜欢这个:
替代文字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); } }
匿名块对于限制variables的范围以及双重大括号的初始化非常有用。 比较:
Set<String> validCodes = new HashSet<String>(); validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D");
有:
Set<String> validCodes = new HashSet<String>() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR5E"); }});
您可以使用块来初始化父范围的最终variables。 这是一个很好的方法来限制一些variables的作用域,只用来初始化单个variables。
public void test(final int x) { final ClassA a; final ClassB b; { final ClassC parmC = getC(x); a = parmC.getA(); b = parmC.getB(); } //... a and b are initialized }
一般情况下,最好将块移入一个方法,但是这种语法对于需要返回多个variables并且不想创build包装类的一次性情况是很好的。