Перечисления Enum в Java - Часть 3
Данная статья:
- написана командой Vertex Academy. Надеемся, что она Вам будет полезна. Приятного прочтения!
- это одна из статей из нашего "Самоучителя по Java"
- Данная статья предполагает, что Вы уже хорошо знаете ООП.
В предыдущей статье (Перечисления Enum в Java - Часть 2) мы познакомились с Enum немного ближе и узнали, что у них есть уже готовые к использованию методы. Но что, если нам недостаточно тех методов, которые предоставляет enum? Об этом Вы узнаете в этой статье.
Мы рассмотрим на конкретных примерах следующее:
1. Конструкторы и переменные
2. Методы в перечислениях
3. Специальные коллекции для перечислений
Как и в предыдущей статье, нам понадобится enum
1 2 3 |
enum Country { CANADA, POLAND, GERMANY } |
Поехали.
1. Конструкторы и переменные в enum
У каждой страны есть своя валюта, чтобы её задать необходимо создать конструктор в enum Country и добавить поле currency
1 2 3 4 5 6 7 8 9 |
enum Country { CANADA("CAD"), POLAND("PLN"), GERMANY("EUR"); String currency; Country(String currency) { this.currency = currency; } } |
Поскольку enum - это класс с дополнительными методами, то к нему применяется все то, что вы уже знаете о классах. Т.е. для enum можно создавать конструкторы, поля и методы.
Допустим у нас появилась страна, у которой еще нет валюты. В этом случае нам необходимо будет добавить конструктор без параметров, или же конструктор по умолчанию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
enum Country { CANADA("CAD"), POLAND("PLN"), GERMANY("EUR"), LAOPAPAS, ZIMKABU(); String currency; Country(String currency) { this.currency = currency; } Country() { } } |
Обратите внимание на значения LAOPAPAS и ZIMKABU(). Использовать конструктор по умолчанию - можно и так и так.
Пример использования enum Currency
1 2 3 |
for (Country country : Country.values()) { System.out.println(country + ", " + country.currency); } |
Вывод в консоль:
1 2 3 4 5 |
CANADA, CAD POLAND, PLN GERMANY, EUR LAOPAPAS, null ZIMKABU, null |
Поля и методы в перечислениях как и в классах могут иметь модификаторы доступа private, protected, default, public, а вот конструкторы в перечислениях всегда будут иметь модификатор private. При попытке указать другой модификатор мы получим ошибку компиляции.
1 2 3 |
public Country(String currency) { // Modifier 'public' is not allowed here this.currency = currency; } |
2. Методы в перечислениях
В перечислениях можно использовать как обычные (общие) так и абстрактные методы, чтобы задать уникальную логику каждому значению перечисления.
Общие методы в перечислениях
Добавим в перечисление метод hasCurrency(), который будет возвращать true или false в зависимости от того, есть валюта у страны или нет
1 2 3 4 5 |
... boolean hasCurrency() { return currency != null; } } |
И рассмотрим использование
1 2 3 4 5 6 7 |
for (Country country : Country.values()) { if (country.hasCurrency()) { System.out.println(country + " has currency, it's " + country.currency); } else { System.out.println(country + " has no currency"); } } |
Вывод в консоль
1 2 3 4 5 |
CANADA has currency, it's CAD POLAND has currency, it's PLN GERMANY has currency, it's EUR LAOPAPAS has no currency ZIMKABU has no currency |
Абстрактные методы в перечислениях
С абстрактными методами немного сложнее, добавим абстрактный метод void sayHello()
1 2 3 |
.... abstract void sayHello(); } |
Теперь нам нужно реализовать этот метод во всех значениях перечисления
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
CANADA("CAD") { @Override void sayHello() { System.out.println("Hello"); } }, POLAND("PLN") { @Override void sayHello() { System.out.println("Cześć"); } }, GERMANY("EUR") { @Override void sayHello() { System.out.println("Hallo"); } }, LAOPAPAS { @Override void sayHello() { System.out.println("Lapapioooo"); } }, ZIMKABU() { @Override void sayHello() { System.out.println("Shakalaka"); } }; |
И посмотрим на использование этого метода.
1 2 3 4 |
for (Country country : Country.values()) { System.out.print(country + " "); country.sayHello(); } |
Вывод в консоль
1 2 3 4 5 |
CANADA Hello POLAND Cześć GERMANY Hallo LAOPAPAS Lapapioooo ZIMKABU Shakalaka |
Если же мы оставим одно значение не переопределённым то получим ошибку компиляции
1 |
ZIMKABU(); 'Country' is abstract; cannot be instantiated |
3. Специальные коллекции для перечислений
Никто нам не запрещает использовать такие коллекции как ArrayList или HashSet вместе с перечислениями, это будет работать точно так же как и с другими значениями. Но раз уж мы знаем наперед значения перечислений, знаем их количество и знаем, что новые значения не будут добавляться в перечисление в работе приложения(в Runtime) т.к. это невозможно, то разработчики придумали специальную коллекцию для них. Эта коллекция работает быстрее и эффективнее обычных, используя, особенности перечислений, которые мы только что описали выше. Эта коллекция называется EnumSet. При работе с enum хорошей практикой является использовать именно коллекцию EnumSet вместо стандартных коллекций.
Примеры создания EnumSet.
Пример 1
1 2 |
Set<Country> countries = EnumSet.allOf(Country.class); System.out.println(countries); //output: [CANADA, POLAND, GERMANY, LAOPAPAS, ZIMKABU] |
Метод allOf() создает EnumSet из всех значений заданого перечисления.
Пример 2
1 2 3 4 5 6 |
EnumSet.of(GERMANY); EnumSet.of(GERMANY, CANADA); EnumSet.of(GERMANY, CANADA, POLAND); EnumSet.of(GERMANY, CANADA, POLAND, LAOPAPAS); EnumSet.of(GERMANY, CANADA, POLAND, LAOPAPAS, ZIMKABU); EnumSet.of(GERMANY, CANADA, POLAND, LAOPAPAS, ZIMKABU, GERMANY); |
Метод of() создает EnumSet, который содержит заданные значения. Метод перегружен, принимая от одного значения до 5 и еще раз перегружен методом of(E first, E... rest), который принимает сколько угодно значений. Всё это нужно для оптимизации и быстродействия EnumSet.
Пример 3
1 2 |
EnumSet<Country> range = EnumSet.range(CANADA, GERMANY); System.out.println(range); //output: [CANADA, POLAND, GERMANY] |
Метод range() создает EnumSet, который содержит все значения перечисления между указанными значениями.
Пример 4
1 2 3 4 |
EnumSet<Country> canadaSet = EnumSet.of(CANADA); Set<Country> germanySet = Set.of(GERMANY); EnumSet<Country> canadaSetCopy = EnumSet.copyOf(canadaSet); EnumSet<Country> germanySetCopy = EnumSet.copyOf(germanySet); |
Метод copyOf() возвращает копию указанного EnumSet, либо же копию любой другой коллекции этого перечисления.
Пример 5
1 2 3 |
EnumSet<Country> initialSet = EnumSet.of(CANADA, POLAND, GERMANY); EnumSet<Country> complement = EnumSet.complementOf(initialSet); System.out.println(complement); //output: [LAOPAPAS, ZIMKABU] |
Изначально, в EnumSet у нас были три страны CANADA, POLAND и GERMANY. Ззатем метод complementOf() создал нам EnumSet который содержит значения LAOPAPAS, ZIMKABU, то есть все значения, которые НЕ содержатся в изначальной коллекции.
Пример 6
1 2 |
EnumSet<Country> noneOf = EnumSet.noneOf(Country.class); System.out.println(noneOf); //output: [] |
Метод noneOf() возвращает пустой EnumSet заданного типа.
P.S.
На самом деле EnumSet это абстрактный класс у которого 2 реализации - RegularEnumSet и JumboEnumSet. Выбор реализации определяется в методах создания EnumSet. Если количество элементов в перечислении, из которого вы хотите создать EnumSet, не превышает 64 то выбран будет RegularEnumSet, если элементов больше 64 - будет выбран JumboEnumSet.
Почему именно 64 элемента? Потому что в RegularEnumSet все значения перечисления умещаются в одну переменную типа long, максимальное колличество битов в котором равно 64. Все операции в RegularEnumSet основаны на булевой логике.
В JumboEnumSet также используется булевая логика, но вместе с массивом значений.
На этом урок заканчивается
За это время мы изучили:
- основы работы с перечислениями
- что такое на самом деле enum
- как добавлять конструкторы, поля и методы в enum
- специальные коллекции для перечислений
Спасибо, что были с нами! 🙂
Надеемся, что наша статья была Вам полезна. Можно записаться к нам на курсы по Java на сайте.