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

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

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

 new ArrayList() {{ add(1); add(2); }}; 

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

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

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

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

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

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

… будет производить эти classы:

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

Это довольно немного накладных расходов для вашего загрузчика 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; } } 

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

Утечка памяти прямо здесь

Изображение с 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"); }}; 

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

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

Например:

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

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

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

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

Больше

Для забавного применения инициализации двойной скобки см. Здесь массив Dwemthy’s в 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 и … коренастый бекон!

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

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

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

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

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

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

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

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

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

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

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

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

Пример:

 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

Это – среди других применений – ярлык для инициализации коллекций. Выучить больше …

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

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

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

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

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

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

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

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

  • Есть ли разница между инициализацией копирования и прямой инициализацией?
  • Как инициализировать структуру в соответствии со стандартами языка программирования C
  • «Ошибка: присваивание выражению с ошибкой типа массива», когда я назначаю поле структуры (C)
  • Соответствуют ли скобки после имени типа с новым?
  • Что такое динамическая инициализация объекта в c ++?
  • Почему не указатели инициализируются с помощью NULL по умолчанию?
  • Какова инициализация по умолчанию массива в Java?
  • Что означают следующие слова в C ++: нуль, инициализация по умолчанию и значение?
  • memset () или инициализацию значения, чтобы обнулить структуру?
  • Возможно, переменная не была инициализирована ошибкой
  • Легкий способ найти неинициализированные переменные-члены
  • Давайте будем гением компьютера.