Java中的匿名代码块
在Java中匿名代码块有什么实际用途吗Java中的匿名代码块,java,Java,在Java中匿名代码块有什么实际用途吗 public static void main(String[] args) { // in { // out } } 请注意,这与命名块无关,即 name: { if ( /* something */ ) break name; } 它们限制了变量的范围 public void foo() { { int i = 10; } System
public static void main(String[] args) {
// in
{
// out
}
}
请注意,这与命名块无关,即
name: {
if ( /* something */ )
break name;
}
它们限制了变量的范围
public void foo()
{
{
int i = 10;
}
System.out.println(i); // Won't compile.
}
然而,在实践中,如果您发现自己正在使用这样一个代码块,那么这可能是您想要将该代码块重构为一个方法的标志。您可以将其用作匿名内部类的构造函数 像这样: 这样可以初始化对象,因为空闲块是在对象构造期间执行的 它不仅限于匿名内部类,也适用于常规类
public class SomeClass {
public List data;{
data = new ArrayList();
data.add(1);
data.add(1);
data.add(1);
}
}
匿名块可用于限制变量的范围,也可用于 比较
Set<String> validCodes = new HashSet<String>();
validCodes.add("XZ13s");
validCodes.add("AB21/X");
validCodes.add("YYLEX");
validCodes.add("AR2D");
Set validCodes=new HashSet();
有效代码。添加(“XZ13s”);
有效代码添加(“AB21/X”);
有效代码。添加(“YYLEX”);
有效代码。添加(“AR2D”);
与
Set validCodes=new HashSet(){{
添加(“XZ13s”);
添加(“AB21/X”);
添加(“YYLEX”);
添加(“AR5E”);
}};
我认为你和/或其他答案混淆了两种不同的句法结构;即实例初始值设定项和块。(顺便说一下,“命名块”实际上是一个带标签的语句,其中语句恰好是一个块。)
实例初始值设定项用于类成员的语法级别;e、 g
public class Test {
final int foo;
{
// Some complicated initialization sequence; e.g.
int tmp;
if (...) {
...
tmp = ...
} else {
...
tmp = ...
}
foo = tmp;
}
}
根据@dfa的示例,初始化器构造最常用于匿名类。另一个用例是对“final”属性进行复杂的初始化;e、 g.参见上面的示例。(但是,使用常规构造函数更常见。上面的模式更常用于静态初始值设定项。)
另一个构造是普通块,出现在代码块中,例如method;e、 g
public void test() {
int i = 1;
{
int j = 2;
...
}
{
int j = 3;
...
}
}
块最常用作控制语句的一部分,用于对语句序列进行分组。但是当您在上面使用它们时,它们(只是)允许您限制声明的可见性;e、 g.j
这通常表明您需要重构代码,但这并不总是明确的。例如,您有时会在用Java编写的解释器中看到这种情况。开关臂中的语句可以分解为单独的方法,但这可能会对解释器的“内部循环”造成显著的性能影响;e、 g
switch (op) {
case OP1: {
int tmp = ...;
// do something
break;
}
case OP2: {
int tmp = ...;
// do something else
break;
}
...
};
@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);
}
代码块不仅尽可能严格地限制了变量的范围(这总是好的,尤其是在处理可变状态和非最终变量时),而且它们还以XML/HTML的方式演示了组件层次结构,使代码更易于阅读、编写和维护
我将每个组件实例化分解成一个方法的问题是
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;
}
}
}
和匿名代码块:
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;
}
}
}
我认为第二版更干净,更容易阅读。而且,它将开关中声明的变量的范围缩小到了声明它们的情况,根据我的经验,99%的时间都是您想要的
但是请注意,它不会改变案例失败的行为-您仍然需要记住包含
中断
或返回
,以防止它 您可以使用块从父范围初始化最终变量。这是一种很好的方法,可以限制一些仅用于初始化单个变量的变量的范围
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
}
一般来说,最好将块移动到方法中,但是当需要返回多个变量并且您不想创建包装类时,这种语法对于一次性情况很好。实例初始值设定项块:
class Test {
// this line of code is executed whenever a new instance of Test is created
{ System.out.println("Instance created!"); }
public static void main() {
new Test(); // prints "Instance created!"
new Test(); // prints "Instance created!"
}
}
class Test {
public static void main() {
{ int i = 20; }
System.out.println(i); // error
}
}
匿名初始值设定项块:
class Test {
class Main {
public void method() {
System.out.println("Test method");
}
}
public static void main(String[] args) {
new Test().new Main() {
{
method(); // prints "Test method"
}
};
{
//=========================================================================
// which means you can even create a List using double brace
List<String> list = new ArrayList<>() {
{
add("el1");
add("el2");
}
};
System.out.println(list); // prints [el1, el2]
}
{
//==========================================================================
// you can even create your own methods for your anonymous class and use them
List<String> list = new ArrayList<String>() {
private void myCustomMethod(String s1, String s2) {
add(s1);
add(s2);
}
{
myCustomMethod("el3", "el4");
}
};
System.out.println(list); // prints [el3, el4]
}
}
}
使用注释或根据代码结构和所选标识符描述任务,然后使用代码块在语言本身不强制的情况下创建层次关系。例如:
public void sendAdminMessage(String msg) throws IOException {
MessageService service; {
String senderKey = properties.get("admin-message-server");
service = MessageService.of(senderKey);
if (!ms.available()) {
throw new MessageServiceException("Not available: " + senderKey);
}
}
/* workaround for issue 1298: Stop sending passwords. */ {
final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
Matcher m = p.matcher(msg);
if (m.matches()) msg = m.group(1) + m.group(2);
}
...
}
上面只是一些示例代码来解释这个概念。第一个块通过其前面的内容“记录”:该块用于初始化服务
变量。第二个块由注释记录。在这两种情况下,块都为注释/变量声明提供了“作用域”:它们解释了特定流程的结束位置。这是一种更常见的样式的替代:
public void sendAdminMessage(String msg) throws IOException {
// START: initialize service
String senderKey = properties.get("admin-message-server");
MessageService service = MessageService.of(senderKey);
if (!ms.available()) {
throw new MessageServiceException("Not available: " + senderKey);
}
// END: initialize service
// START: workaround for issue 1298: Stop sending passwords.
final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
Matcher m = p.matcher(msg);
if (m.matches()) msg = m.group(1) + m.group(2);
// END: workaround for issue 1298: Stop sending passwords.
...
}
尽管这些块更好:它们允许您使用编辑器工具更有效地导航(“转到块的末尾”),它们限定块内使用的局部变量的范围,以便它们无法逃逸,最重要的是,它们与包含的概念保持一致:作为java程序员,您已经很熟悉,使用包含的概念:对于块、if块、方法块:它们都是代码流中层次结构的表达式。出于文件原因而非技术原因对代码的遏制仍然是遏制。为什么要使用不同的机制?一致性是有用的。减少精神负担