JavaRush /Java блог /Random UA /Перерахування до Java (java enum)
articles
15 рівень

Перерахування до Java (java enum)

Стаття з групи Random UA
Програмуючи ми часто стикаємося з необхідністю обмежити безліч допустимих значень деякого типу даних. Так, наприклад, день тижня може мати 7 різних значень, місяць на рік – 12, а пора року – 4. Для вирішення подібних завдань у багатьох мовах програмування зі статичною типізацією передбачено спеціальний тип даних – перерахування ( ) enum. У Java перелік виник не відразу. Спеціалізовану мовну конструкцію enumбуло введено починаючи з версії 1.5. До цього моменту програмісти використовували інші методи реалізації перерахувань. Перерахування до Java (java enum) - 1

Конструкція enum

Почнемо із прикладу. Давайте опишемо за допомогою enumтип даних для зберігання пори року:
enum Season { WINTER, SPRING, SUMMER, AUTUMN }
Ну і простий приклад його використання:
Season season = Season.SPRING;
if (season == Season.SPRING) season = Season.SUMMER;
System.out.println(season);
В результаті виконання якого на консоль буде виведено SUMMER . Думаю, що приклад очевидний і пояснення не потребує.

Перерахування – це клас

Оголошуючи enumми неявно створюємо клас похідний від java.lang.Enum. Умовно конструкція enum Season { ... }еквівалентна class Season extends java.lang.Enum { ... }. І хоча явно успадковуватися від java.lang.Enumнас не дозволяє компілятор, все ж у тому, що enumуспадковується, легко переконатися за допомогою reflection:
System.out.println(Season.class.getSuperclass());
На консоль буде виведено:
class java.lang.Enum
Власне успадкування за нас автоматично виконує компілятор Java. Далі давайте умовимося називати клас, створений компілятором для реалізації перерахування enum-класом, а можливі значення перерахованого типу - елементами enum-a.

Елементи перерахування - екземпляри enum-класу, доступні статично

Елементи enum Season (WINTER, SPRING и т.д.)- це статично доступні екземпляри enum-класу Season. Їхня статична доступність дозволяє нам виконувати порівняння за допомогою оператора порівняння посилань ==. Приклад:
Season season = Season.SUMMER;
if (season == Season.AUTUMN) season = Season.WINTER;

Назва та порядковий номер елемента enum

Як було сказано раніше будь-який enum-клас успадковує java.lang.Enum, який містить низку методів корисних всім перерахувань. Приклад:
Season season = Season.WINTER;
System.out.println("season.name()=" + season.name() + " season.toString()=" + season.toString() + " season.ordinal()=" + season.ordinal());
Буде виведено:
season.name()=WINTER season.toString()=WINTER season.ordinal()=0
Тут показано використання методів name(), toString()та ordinal(). Семантика методів очевидна. Слід звернути увагу, що ці методи enum-клас успадковує з класу java.lang.Enum. Отримання елемента enumза рядковим поданням його імені Досить часто виникає завдання отримати елемент enumза його рядковим поданням. Для цих цілей у кожному enumкласі компілятор автоматично створює спеціальний статичний метод: public static EnumClass valueOf(String name), який повертає елемент перерахування EnumClassз назвою, що дорівнює name. Приклад використання:
String name = "WINTER";
Season season = Season.valueOf(name);
В результаті виконання коду змінна season дорівнюватиме Season.WINTER. Варто звернути увагу, що якщо елемент не буде знайдений, то буде викинуто IllegalArgumentException , а якщо nameдорівнює nullNullPointerException . Про це часто забувають. Чомусь багато хто твердо впевнені, що якщо функція приймає один аргумент і за деяких умов викидає IllegalArgumentException , то при передачі туди null, також буде неодмінно викинутий IllegalArgumentException . Але це не стосується справи. Продовжимо. Отримання всіх елементів переліку Іноді необхідно отримати список всіх елементів enumкласу під час виконання. Для цих цілей у кожномуenum-Класі компілятор створює метод public static EnumClass[] values(). Приклад використання:
System.out.println(Arrays.toString(Season.values()));
Отримаємо висновок:
[WINTER, SPRING, SUMMER, AUTUMN]
Зверніть увагу, що ні метод valueOf(), ні метод values()не визначений у класі java.lang.Enum. Натомість вони автоматично додаються компілятором на етапі компіляції enum-класу. Додаємо свої методи до enum-класу У Вас є можливість додавати власні методи як до enum-класу, так і до його елементів: Перерахування в Java (java enum) - 2Те саме, але з поліморфізмом: Перерахування до Java (java enum) - 3Останній приклад демонструє використання успадкування в enum. Про це далі. Спадкування вenum За допомогою enumJava можна реалізувати ієрархію класів, об'єкти якої створюються в єдиному екземплярі і доступні статично. При цьому елементи enumможуть утримувати власні конструктори. Наведемо приклад: Перерахування до Java (java enum) - 4Тут оголошується перелікTypeз трьома INTелементами INTEGERі STRING. Компілятор створить такі класи та об'єкти:
  • Type- Похідний клас відjava.lang.Enum
  • INT- Об'єкт 1-го класу похідного відType
  • INTEGER- Об'єкт 2-го класу похідного відType
  • STRING- Об'єкт 3-го класу похідного відType
Три похідні класу будуть створені з поліморфним методом Object parse(String)та конструктором Type(..., boolean). При цьому об'єкти класів INTі існують в єдиному екземплярі і доступні статично INTEGER. STRINGУ цьому можна переконатись:
System.out.println(Type.class);
System.out.println(Type.INT.getClass() + " " + Type.INT.getClass().getSuperclass());
System.out.println(Type.INTEGER.getClass() + " " + Type.INTEGER.getClass().getSuperclass());
System.out.println(Type.STRING.getClass()  + " " + Type.STRING.getClass().getSuperclass());
Отримаємо висновок:
class Type
class Type$1 class Type
class Type$2 class Type
class Type$3 class Type
Видно, що компілятор створив клас Typeі три nestedкласи, похідних від Type.

Декомпільований enum-class із успадкуванням

На підтвердження вищенаведеному наведемо ще результат декомпіляції перерахування Typeз прикладу вище: Перерахування в Java (java enum) - 5

Перерахування та параметричний поліморфізм

У читача може виникнути питання: " Чому вищезгадане перерахування Type не використовує генерики (generics)? ". Справа в тому, що в Java використання генериків enumзаборонено. Так наступний приклад не скомпілюється:
enum Type<T> {}

Подальше вивчення

Для більш глибокого розуміння того, як працюють перерахування в Java, рекомендую ознайомитися з вихідними кодами класу java.lang.Enum, а також скористатися декопмілятором Jad для вивчення згенерованого коду. Більш того, вивчення вихідних кодів бібліотеки Java абсолютно необхідне для розуміння принципів роботи багатьох механізмів Java і корисне як еталон об'єктно-орієнтованого дизайну. Посилання на першоджерело: http://alexander.lds.lg.ua/
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ