Что такое инициализация двойной скобки в Java? - PullRequest
265 голосов
/ 24 декабря 2009

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

Ответы [ 12 ]

258 голосов
/ 17 декабря 2014

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

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

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"); }};

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

255 голосов
/ 24 декабря 2009

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

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

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

36 голосов
/ 05 августа 2015
  • Первая фигурная скобка создает новый анонимный внутренний класс.
  • Второй набор фигурных скобок создает инициализаторы экземпляра, такие как статический блок в Class.

Например:

   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, поэтому этот внутренний класс может использовать метод put ().

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

более

22 голосов
/ 24 декабря 2009

Интересное приложение инициализации двойной скобки смотрите здесь Массив Двемти в 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; толстый бекон!

12 голосов
/ 20 декабря 2016

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

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

{
    // whatever code is needed for initialization goes here
}
8 голосов
/ 28 ноября 2015

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

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

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

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

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

8 голосов
/ 04 сентября 2015

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

  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 builder за все время.

Посмотреть все примеры на github: java-sf-builder-simple-example

4 голосов
/ 20 июля 2014

Вы можете поместить некоторые операторы 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));
    }
};

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

4 голосов
/ 29 марта 2011

ты имеешь в виду что-то подобное?

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

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

4 голосов
/ 24 декабря 2009

Это, среди прочего, ярлык для инициализации коллекций. Узнать больше ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...