Програмуючи ми часто стикаємося з необхідністю обмежити безліч допустимих значень деякого типу даних. Так, наприклад, день тижня може мати 7 різних значень, місяць на рік – 12, а пора року – 4. Для вирішення подібних завдань у багатьох мовах програмування зі статичною типізацією передбачено спеціальний тип даних – перерахування ( ) Елементи перерахування - екземпляри
Елементи
enum
. У Java перелік виник не відразу. Спеціалізовану мовну конструкцію enum
було введено починаючи з версії 1.5. До цього моменту програмісти використовували інші методи реалізації перерахувань.
Конструкція 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
дорівнює null
— NullPointerException . Про це часто забувають. Чомусь багато хто твердо впевнені, що якщо функція приймає один аргумент і за деяких умов викидає 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
-класу, так і до його елементів: Те саме, але з поліморфізмом: Останній приклад демонструє використання успадкування в enum
. Про це далі. Спадкування вenum
За допомогою enum
Java можна реалізувати ієрархію класів, об'єкти якої створюються в єдиному екземплярі і доступні статично. При цьому елементи enum
можуть утримувати власні конструктори. Наведемо приклад: Тут оголошується перелік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
з прикладу вище:
Перерахування та параметричний поліморфізм
У читача може виникнути питання: " Чому вищезгадане перерахування Type не використовує генерики (generics)? ". Справа в тому, що в Java використання генериківenum
заборонено. Так наступний приклад не скомпілюється:
enum Type<T> {}
Подальше вивчення
Для більш глибокого розуміння того, як працюють перерахування в Java, рекомендую ознайомитися з вихідними кодами класуjava.lang.Enum
, а також скористатися декопмілятором Jad для вивчення згенерованого коду. Більш того, вивчення вихідних кодів бібліотеки Java абсолютно необхідне для розуміння принципів роботи багатьох механізмів Java і корисне як еталон об'єктно-орієнтованого дизайну. Посилання на першоджерело: http://alexander.lds.lg.ua/
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ