1. Double brace инициализация
Инициализация с использованием двойных фигурных скобок (далее по тексту — Double brace инициализация) — Java средство для создания коллекций таких как list, set и map одновременно с их объявлением.Когда вам необходимы списки с фиксированными элементами, такие как перечень поддерживаемых продуктов или денежных единиц, то объявление списка одновременно с его инициализацией улучшает читабельность кода. Именно поэтому Double brace инициализация набирает популярность, так как иных стандартных способов создания с одновременной инициализацией в коде для коллекций нет.
К сожалению, в отличие от других языков программирования Java не поддерживает коллекции литералов. В соответствие с данным ограничением, создание неизменяемого списка (unmodifiableList) даже с небольшим количеством элементов вынуждает нас писать много строк кода в виде повторяющихся вызовов
add()
для добавления нужных элементов с завершающим обертыванием (wrapping):
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(5);
list.add(7);
List<Integer> unmodifiableList = Collections.unmodifiableList(list);
Это излишне избыточное описание, которое можо упростить. Заполним статические списки удобным для нас способом, а именно непосредственно в статических блоках при инициализации, в чем нам и поможет Double brace
инициализация, позволяющая нам записать все в одну строку:
List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>() {{
add(2);
add(3);
add(5);
}});
Аналогично Double brace
инициализация поможет нам заполнить значениями и HashMap
:
Map<Integer, String> intToString = new HashMap<Integer, String>(){{
put(1, "one");
put(2, "two");
put(3, "three");
}};
Все это так элегантно выглядит, однако имеет свои недостатки, которые и делают Double brace инициализацию анти-шаблоном. Рассмотрим их далее в следующей главе.
Плюсы и минусы Double brace инициализации.
Double
brace инициализация использует создание внутреннего анонимного класса (anonymous inner class). Что поначалу внешне скрыто, однако Double brace
инициализация создает класс с дальнейшей инициализацией его экземпляра, каждый раз как вы применяете ее. Помимо этого используется скрытая ссылка на этот закрытый класс, что может привести нас к возможным утечкам памяти. Вы также не сможете использовать оператор привидения для generic-ов (diamond operator < >), так как мы не можем обратиться внутрь анонимного класса.
(От переводчика: Еще раз более подробно: после первой { создается внутренний анонимный класс, после второй { происходит инициализация при создании экземпляра класса при которой мы имеем доступ к полям и методам внешнего (по отношению к анонимному) класса.)
|
- Уменьшение строк в коде
- Создание и инициализация в одном выражении.
- Скрытое от вас создание анонимного класса.
- Что стоит нам дополнительных расходов для его экземпляра каждый раз при использовании.
- Каждый раз создается скрытая ссылка на него, которая возможно приведет к утечкам памяти.
Альтернативы Double brace инициализации в Java
Хорошая новость состоит в том, что имеются иные способы достижения тех же самых целей в Java. Мы может реализовать в одной строке кода создание и инициализацию значениями ArrayList используя Copy constructor из Collection класса как показано ниже:
List<Integer> 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<String> list = Collections.unmodifiableList(Stream.of("abc", "bcd", "cde").collect(toList()));
Для Set
вы можете использовать Collectors.toSet()
метод вместо Collectors.toList()
, как показано ниже:
Set<String> 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=конструктор). |
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Мне только кажется, что гораздо чаще переводят «anti pattern» как «антипаттерн», а не как «анти-шаблон».