这个带有以下双大括号的构造函数调用是什么?

2022-09-03 00:22:42

不幸的是,我已经有五年没有编写Java代码了,我绝对不记得下面的代码是如何或为什么工作的。

我偶然发现了一个类似的例子,并将其分解为这个。重点是注释下面的部分:我没有得到构造函数符号,后面跟着双括号中的块。不幸的是,我无法在Java文档或使用Google中找到任何内容(我应该谷歌哪个单词?)。

package syntaxtest;

public class Main {

    public static void main(String[] args) {

        // What kind of notation is this?
        MyTest tester = new MyTest() {{
            setName("John Johnson");
        }};

        System.out.println(tester.getName());
    }
}


class MyTest {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

所以这是我的问题:

  1. 如何调用此表示法/语法?
  2. 我在哪里可以阅读有关它的一些文档?

我想/希望如果有人能为我提供第一个问题的答案,我将能够自己回答第二个问题。

明确一点:我知道输出;)但我不知道为什么它有效。John Johnson


答案 1

这称为双括号初始化

第一个大括号创建一个新的 AnonymousInnerClass,第二个大括号声明一个实例初始值设定项块,该块在实例化匿名内部类时运行。这种类型的初始值设定项块正式称为“实例初始值设定项”,因为它是在类的实例范围内声明的 - “静态初始值设定项”是一个相关的概念,其中关键字static放在启动块的大括号之前,并在类装入器完成装入类后立即在类级别执行(在 http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.6) 初始值设定项块可以使用包含范围中可用的任何方法、字段和最终变量,但必须警惕初始值设定项在构造函数之前运行的事实。

这仅适用于非最终类,因为它会创建一个匿名子类。


答案 2

让我们以不同的方式布局代码:

MyTest tester = new MyTest() {
  {
    setName("John Johnson");
  }
};

您在此处看到的称为双大括号初始化。您有一个 匿名的内部子类 类 ,以及一个初始值设定项块,该块包含构造对象时运行的代码。MyTest

通常,您会将此类代码放在构造函数中,但由于匿名内部类不能具有构造函数,因此这是保证代码在应该运行时运行的唯一方法。

话虽如此,这样做有点丑陋。有更好的方法。但是,我自己偶尔会使用它,通常在下面的习语中创建一个不可变的映射:

final Map<String, Integer> textToInt = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
    put("one", 1);
    put("two", 2);
    // etc
}});

这将创建一个新映射,覆盖它,在初始值设定项块中向其添加一些值,并将其包装在不可修改的映射中。