Что это за вызов конструктора со следующими двойными фигурными скобками?

К сожалению, я не кодировал Java около пяти лет, и я абсолютно не помню, как и почему работает следующий код.

Я наткнулся на подобный пример и сломал его. Акцент делается на части ниже комментария: я не получаю нотацию конструктора, за которой следует блок в двойных скобках. И, к сожалению, я не могу найти что-либо в документации Java или с помощью Google (какие слова должны быть у 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;
    }
}

Итак, вот мои вопросы:

  • Как называется это обозначение/синтаксис?
  • Где я могу прочитать некоторую документацию об этом?

Я думаю/надеюсь, что смогу ответить на второй вопрос сам, если кто-нибудь может дать мне ответ на первый вопрос.

Чтобы было ясно: я знаю, что результат 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
}});

Что создает новую карту, переопределяет ее, добавляет в нее значения в блок инициализатора и обертывает ее в немодифицируемую карту.

Ответ 3

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

совпадает с

MyTest tester = new MyTest();
tester.setName("John Johnson");