Joysi
41 уровень

Double brace инициализация

Пост из группы Архив info.javarush.ru
3267 участников

1. Double brace инициализация

Перевод статьи What is Double Brace Initialization in Java? Anti Pattern Example (опубликованной в октябре 2015 года). Инициализация с использованием двойных фигурных скобок (далее по тексту - Double brace инициализация) – Java средство для создания коллекций таких как list, set и map одновременно с их объявлением. Когда вам необходимы списки с фиксированными элементами, такие как перечень поддерживаемых продуктов или денежных единиц, то объявление списка одновременно с его инициализацией улучшает читабельность кода. Именно поэтому Double brace инициализация набирает популярность, так как иных стандартных способов создания с одновременной инициализацией в коде для коллекций нет. К сожалению, в отличие от других языков программирования Java не поддерживает коллекции литералов. В соответствие с данным ограничением, создание неизменяемого списка (unmodifiableList) даже с небольшим количеством элементов вынуждает нас писать много строк кода в виде повторяющихся вызовов add() для добавления нужных элементов с завершающим обертыванием (wrapping): List list = new ArrayList<>(); list.add(2); list.add(3); list.add(5); list.add(7); List unmodifiableList = Collections.unmodifiableList(list); Это излишне избыточное описание, которое можо упростить. Заполним статические списки удобным для нас способом, а именно: непосредственно в статических блоках при инициализации, в чем нам и поможет Double brace инициализация, позволяющая нам записать все в одну строку: List list = Collections.unmodifiableList(new ArrayList() {{ add(2); add(3); add(5); }}); Аналогично Double brace инициализация поможет нам заполнить значениями и HashMap: Map intToString = new HashMap(){{ put(1, "one"); put(2, "two"); put(3, "three"); }}; Все это так элегантно выглядит, однако имеет свои недостатки, которые и делают Double brace инициализацию анти-шаблоном. Рассмотрим их далее в следующей главе.

Плюсы и минусы Double brace инициализации.

Double brace инициализация использует создание внутреннего анонимного класса (anonymous inner class). Что поначалу внешне скрыто, однако Double brace инициализация создает класс с дальнейшей инициализацией его экземпляра, каждый раз как вы применяете ее. Помимо этого используется скрытая ссылка на этот закрытый класс, что может привести нас к возможным утечкам памяти. Вы также не сможете использовать оператор привидения для generic-ов (diamond operator < >), так как мы не можем обратиться внутрь анонимного класса. (От переводчика: Еще раз более подробно: после первой { создается внутренний анонимный класс, после второй { происходит инициализация при создании экземпляра класса при которой мы имеем доступ к полям и методам внешнего (по отношению к анонимному) класса.) Плюсы:
  1. Уменьшение строк в коде
  2. Создание и инициализация в одном выражении.
Минусы:
  1. Скрытое от вас создание анонимного класса.
  2. Что стоит нам дополнительных расходов для его экземпляра каждый раз при использовании.
  3. Каждый раз создается скрытая ссылка на него, которая возможно приведет к утечкам памяти.
Вердикт: Из-за вышеуказанных минусов и существованию альтернатив Double brace инициализация рассматривается как анти-шаблон в мире Java. Спасите котенка

Альтернативы Double brace инициализации в Java.

Хорошая новость состоит в том, что имеются иные способы достижения тех же самых целей в Java. Мы может реализовать в одной строке кода создание и инициализацию значениями ArrayList используя Copy constructor из Collection класса как показано ниже: List list = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(2, 3, 5))); Arrays.asList() возвратит нам список фиксированной длины переданный в ArrayList copy constructor. Помните про различие между списками фиксированной длины возвращаемыми из Arrays.asList() и Collections.unmodifiableList(): Вы не можете добавлять или удалять элементы ArrayList-а, однако вы можете изменить элемент по индексу с использованием set(), чего вам не удастся сделать со списком, возвращенным Collections.unmodifiableList(). Если вы хотите получить небольшой список – это лучший способ, хотя он будет менее прозрачным для Set и других коллекций, так придется создать List перед созданием Set-а. Но это все же лучше, нежели Double brace инициализация, так как в данном случае не создается лишний внутренний анонимный класс при каждом подобном использовании. Если вы работаете под Java 8 у вас есть еще один альтернативный способ. JDK 8 Stream API поможет вам создавать небольшие коллекции комбинируя вывод Stream Factory методы в коллекцию List: List list = Collections.unmodifiableList(Stream.of("abc", "bcd", "cde").collect(toList())); Для Set вы можете использовать Collectors.toSet() метод вместо Collectors.toList(), как показано ниже: Set set = Collections.unmodifiableSet(Stream.of("abc", "bcd", "cde").collect(toSet())); Кстати, Stream collect методы не гарантируют, что сгенерированные ими коллекции защищены от изменений. В Java 8 коллекции, которые они вернули (такие как - ArrayList, HashSet, and HashMap ), вполне себе обычные ( мы можем менять их), но это данный факт возможно будет исправлен в будущих релизах JDK. Это все на данный момент о Double brace инициализации в Java. Этот шаблон приемлим для тестов и демонстраций, но не достаточно хорош для использования в промышленной эксплуатации (production). Из-за присущих ему минусов, Double brace инициализация стал анти-шаблоном в наши дни, особенно учитывая доступные альтернативы. Сам я все еще использую данную конструкцию для инициализации static map-ов и все. Для List я предпочитаю создавать Collections комбинируя с созданием Array.asList в его конструкторе. А если использую Java 8 – конструкцию с использованием Stream API и collect(). Статьи по теме: Если вам понравился данный учебный материал и вы хотите узнать побольше о паттернах, принципах и лучших приемах программирования на Java, возможно вы также ознакомитесь и с другими статьями на нашем сайте. Рекомендуемая литература: Если вы хотите побольше узнать о шаблонах и лучших практиках вы должны прочитать книгу «Эффективное программирование» Джошуа Блоха, ни одна книга не может занять ее место. А если вы уже поднаторели в Java и ищете книгу по шаблонам проектирования, юморной стиль изложения которой интересно и легко читать – обратите внимание на «Нead First. Шаблоны проектирования». От переводчика: Я умышленно привел ссылку на оригинальную книгу Блоха, так как перевод ее на русский неудачен (например, Builder=конструктор).
Комментарии (6)
  • популярные
  • новые
  • старые
Для того, что бы оставить комментарий вы должны авторизироваться
Exidnus 38 уровень, Санкт-Петербург
6 февраля 2016, 20:01
Спасибо, интересно.
Мне только кажется, что гораздо чаще переводят «anti pattern» как «антипаттерн», а не как «анти-шаблон».
Joysi 41 уровень
6 февраля 2016, 22:28
Спасибо. Я тоже над этим думал. Как и все англоязычные тех термины их иногда просто «кириллизируют» без перевода. Остановился на шаблон из-за wikipedia где как основной (в применении к pattern-у проектирования) идет слово шаблон. В переведенных книгах встречает и так и этак. Попробую в дальнейшем вообще использовать pattern.
Как по вашему, если в тексте будет идти… pattern проектирования Builder… — не будет дискомфорта при чтении? Но вот Builder бы я точно не переводил.
driver 41 уровень
8 февраля 2016, 16:03
Как по вашему, если в тексте будет идти… pattern проектирования Builder… — не будет дискомфорта при чтении?
Не будет
Sygurny 26 уровень
13 февраля 2016, 11:52
А как по мне так слово «шаблон» звучит лучше.
Exidnus 38 уровень, Санкт-Петербург
13 февраля 2016, 17:10
Да это дело вкуса, пожалуй.
Joysi 41 уровень
4 февраля 2016, 16:21
Мой первый перевод статьи с английского языка. Критика приветствуется.