Что такое инициализация Double Brace в Java?

Что такое синтаксис инициализации Double Brace ({{ ... }}) в Java?

Ответ 1

Инициализация двойной скобки создает анонимный класс, полученный из указанного класса (внешние фигурные скобки), и предоставляет блок инициализации внутри этого класса (внутренние фигурные скобки). например.

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

Обратите внимание, что эффект использования этой двойной инициализации состоит в том, что вы создаете анонимные внутренние классы. Созданный класс имеет неявный this указатель на окружающий внешний класс. Хотя обычно это не проблема, это может вызвать горе в некоторых обстоятельствах, например. при сборке или сборе мусора, и это стоит знать об этом.

Ответ 2

Каждый раз, когда кто-то использует двойную привязку, котенка убивают.

Помимо синтаксиса, довольно необычного и не очень идиоматического (вкус, конечно, дискуссионный), вам необязательно создавать две существенные проблемы в вашем приложении которые я только недавно blogged о более подробно здесь.

1. Вы создаете слишком много анонимных классов

Каждый раз, когда вы используете инициализацию двойной скобки, создается новый класс. Например. этот пример:

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... создаст эти классы:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

Это довольно немного накладных расходов для вашего загрузчика классов - ни за что! Конечно, это не займет много времени для инициализации, если вы это сделаете один раз. Но если вы делаете это 20 000 раз по всему вашему корпоративному приложению... все, что куча памяти только для немного "синтаксического сахара"?

2. Вы потенциально создаете утечку памяти!

Если вы берете приведенный выше код и возвращаете эту карту из метода, вызывающие его методы могут быть ничего не подозревающими на очень тяжелых ресурсах, которые не могут быть собраны в мусор. Рассмотрим следующий пример:

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

Возвращаемый Map теперь будет содержать ссылку на прилагаемый экземпляр ReallyHeavyObject. Вероятно, вы не хотите рисковать тем, что:

Memory Leak Right Here

Изображение из http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/

3. Вы можете притворяться, что у Java есть литералы по картам

Чтобы ответить на ваш реальный вопрос, люди использовали этот синтаксис, чтобы притворяться, что Java имеет что-то вроде картографических литералов, похожих на существующие литералы массивов:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

Некоторые люди могут найти это синтаксически стимулирующее.

Ответ 3

  • Первая скобка создает новый анонимный внутренний класс.
  • Второй набор скобок создает инициализаторы экземпляра, такие как статический блок в классе.

Например:

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set<String> keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

Как это работает

Первая скобка создает новый анонимный внутренний класс. Эти внутренние классы способны получить доступ к поведению своего родительского класса. Итак, в нашем случае мы фактически создаем подкласс класса HashSet, поэтому этот внутренний класс способен использовать метод add().

И Второй набор фигурных скобок - не что иное, как инициализаторы экземпляров. Если вы напомните основные концепции Java, вы можете легко связать блоки инициализатора экземпляра со статическими инициализаторами из-за аналогичной структуры, подобной структуре. Единственное отличие состоит в том, что статический инициализатор добавляется с ключевым словом static и запускается только один раз; независимо от того, сколько объектов вы создаете.

подробнее

Ответ 4

Для веселого применения инициализации двойной скобки см. здесь Dwemthys Array в Java.

Отрывок

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

А теперь будьте готовы к BattleOfGrottoOfSausageSmells и & hellip; коренастый бекон!

Ответ 5

Я считаю важным подчеркнуть, что в Java нет такой вещи, как "Инициализация двойного брака". Веб-сайт Oracle не имеет этого термина. В этом примере используются две функции: анонимный класс и блок инициализатора. Похоже, что старый блок инициализатора был забыт разработчиками и вызвал некоторую путаницу в этой теме. Ссылка из Документы Oracle:

Блоки инициализатора для переменных экземпляра выглядят так же, как и статические блоки инициализатора, но без статического ключевого слова:

{
    // whatever code is needed for initialization goes here
}

Ответ 6

1- Не существует такой вещи как двойные скобки:
Я хотел бы отметить, что нет такой вещи как инициализация двойной скобки. Существует только нормальный традиционный один блок инициализации фигурной скобки. Второй блок скобок не имеет ничего общего с инициализацией. Ответы говорят, что эти две скобки что-то инициализируют, но это не так.

2- Это не только анонимные классы, но и все классы:
Почти все ответы говорят о том, что это вещь, используемая при создании анонимных внутренних классов. Я думаю, что у людей, читающих эти ответы, будет впечатление, что это используется только при создании анонимных внутренних классов. Но это используется во всех классах. Чтение этих ответов, похоже, - какое-то совершенно новое будущее, посвященное анонимным занятиям, и я думаю, что это вводит в заблуждение.

3- Цель состоит в том, чтобы поставить скобки друг за другом, а не новую концепцию:
Далее, этот вопрос говорит о ситуации, когда вторая открывающая скобка находится сразу после первой открывающей скобки. При использовании в обычном классе обычно есть некоторый код между двумя скобками, но это абсолютно одно и то же. Так что это вопрос размещения скобок. Так что я думаю, что мы не должны говорить, что это какая-то новая захватывающая вещь, потому что это то, что мы все знаем, но просто написано с некоторым кодом в скобках. Мы не должны создавать новую концепцию под названием "инициализация двойной скобки".

4- Создание вложенных анонимных классов не имеет ничего общего с двумя фигурными скобками:
Я не согласен с аргументом, что вы создаете слишком много анонимных классов. Вы создаете их не из-за блока инициализации, а только потому, что создаете их. Они будут созданы, даже если вы не использовали инициализацию с двумя фигурными скобками, чтобы эти проблемы возникали даже без инициализации... Инициализация не является фактором, который создает инициализированный объект.

Кроме того, мы не должны говорить о проблеме, созданной с помощью этой несуществующей вещи "инициализация двойной скобкой" или даже обычной инициализацией в одну скобку, потому что описанные проблемы существуют только из-за создания анонимного класса, поэтому он не имеет ничего общего с исходным вопросом. Но все ответы дают читателям впечатление, что это не вина создания анонимных классов, а злая (несуществующая) вещь, называемая "инициализация двойной скобкой".

Ответ 7

Чтобы избежать всех негативных последствий инициализации двойной скобки, например:

  1. Сломанная "равная" совместимость.
  2. При выполнении прямых заданий не выполняется никаких проверок.
  3. Возможные утечки памяти.

делайте следующее:

  1. Создайте отдельный класс "Builder", особенно для инициализации двойной комбинации.
  2. Объявлять поля со значениями по умолчанию.
  3. Поместите метод создания объекта в этот класс.

Пример:

public class MyClass {
    public static class Builder {
        public int    first  = -1        ;
        public double second = Double.NaN;
        public String third  = null      ;

        public MyClass create() {
            return new MyClass(first, second, third);
        }
    }

    protected final int    first ;
    protected final double second;
    protected final String third ;

    protected MyClass(
        int    first ,
        double second,
        String third
    ) {
        this.first = first ;
        this.second= second;
        this.third = third ;
    }

    public int    first () { return first ; }
    public double second() { return second; }
    public String third () { return third ; }
}

Использование:

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

Преимущества:

  1. Просто использовать.
  2. Не нарушает совместимость "равна".
  3. Вы можете выполнять проверки в методе создания.
  4. Нет утечек памяти.

Недостатки:

  • Никто.

И, как результат, у нас самый простой шаблон java-строителя.

Просмотреть все образцы в github: java-sf-builder-simple-example

Ответ 8

Он - среди других применений - ярлык для инициализации коллекций. Подробнее...

Ответ 9

вы имеете в виду что-то вроде этого?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

это инициализация списка массивов во время создания (hack)

Ответ 10

Вы можете поместить некоторые операторы Java в цикл для инициализации коллекции:

List<Character> characters = new ArrayList<Character>() {
    {
        for (char c = 'A'; c <= 'E'; c++) add(c);
    }
};

Random rnd = new Random();

List<Integer> integers = new ArrayList<Integer>() {
    {
         while (size() < 10) add(rnd.nextInt(1_000_000));
    }
};

Но этот случай влияет на производительность, проверьте этот обсуждение

Ответ 11

Инициализация двойной привязки использует внутренний синтаксис класса. Предположим, вы хотите построить список массивов и передать его методу:

ArrayList<String> friends = new ArrayList<>();
friends.add("Mark");
friends.add("Steve");
invite(friends);

Если вам снова не нужен список массивов, было бы неплохо сделать его анонимным. Но тогда как вы можете добавить элементы? (здесь начинается инициализация двойной привязки) Вот как это сделать:

invite(new ArrayList<String>({{ add("Mark"); add("Steve");}});

Обратите внимание на двойные фигурные скобки. Внешние фигурные скобки создают анонимный подкласс ArrayList. Внутренние фигурные скобки являются блоком построения объекта.

Ответ 12

Это будет выглядеть так же, как и ключевое слово с таким популярным в flash и vbscript. Это способ изменения того, что this, и ничего более.

Ответ 13

Как указано в @Lukas Eder, двойные скобки требуют инициализации коллекций.

Он создает анонимный внутренний класс, и поскольку все внутренние классы сохраняют ссылку на родительский экземпляр, он может - и, вероятно, 99% - предотвратить сбор мусора, если на эти объекты коллекции ссылаются больше объектов, чем просто декларирование.

Java 9 представила удобные методы List.of, Set.of и Map.of, которые следует использовать вместо этого. Они быстрее и эффективнее, чем инициализатор с двойной привязкой.