Привіт! Сьогодні ми поговоримо про особливий тип даних у Java – Enum (скорочено від англійського enumeration – "перелік").
У чому ж полягає їхня особливість?
Давай уявимо, що нам потрібно реалізувати в програмі місяці.
Здавалося б, у чому проблема? Треба просто визначити, які властивості є у будь-якого місяця. Мабуть, нам потрібні насамперед назва місяця і кількість днів у ньому.
Вирішення завдання виглядає досить простим:
Наостанок хочу порекомендувати тобі одну вкрай корисну книгу з Java, а саме – "Effective Java" Джошуа Блоха.
Автор – один із творців Java, тож його порадам щодо правильного і грамотного використання засобів мови точно можна довіряти :)
Щодо нашої лекції, раджу тобі звернути особливу увагу на розділ книги, присвячений enum.
Продуктивного тобі читання! :)

public class Month {
private String name;
private int daysCount;
public Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\''' +
", daysCount=" + daysCount +
'}';
}
}
Будь ласка, повний набір!
У нас є клас Month, необхідні поля, геттери-сеттери, toString(). Хіба що equals() і hashCode() треба додати для повного щастя :) Однак, у нас є концептуальна проблема.
Як ти, напевно, пам'ятаєш, одна з головних переваг ООП полягає в тому, що воно дає змогу легко моделювати сутності з реального світу.
Стілець, машина, планета – всі ці поняття зі звичайного життя легко уявити в програмі за допомогою абстракції.
Проблема в тому, що у деяких сутностей у реальному світі є суворо обмежене коло значень.
У році всього 4 пори року.
У музиці всього 7 нот.
У календарі всього 12 місяців.
У Оушена всього 11 друзів (хоча, це суперечливе питання :))
Іншими словами, звичайний Java-клас не здатний змоделювати ці сутності та дотримати їхні природні обмеження.
У нашому класі Month є всі необхідні поля. Але ж якщо його використовуватиме інший програміст, ніхто не завадить йому створювати абсолютно божевільні об'єкти:
public class Main {
Month month1 = new Month("lol", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
Якщо таке з'явиться в програмі, буде непросто знайти винного! З одного боку, програміст, який створив об'єкти, міг би і зрозуміти, що клас Month має на увазі "місяць у році", і не писати подібну нісенітницю.
З іншого боку, він лише користувався тими можливостями, які йому надав проєктувальник класу. Можна призначити будь-які імена та кількість днів? Він і призначив.
Що ж у такій ситуації робити?
До виходу версії мови Java 1.5 програмістам доводилося, відверто кажучи, викручуватися :)
У ті часи вони створювали ось такі конструкції:
public class Month {
private String name;
private int daysCount;
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month JANUARY = new Month("Січень", 31);
public static Month FEBRUARY = new Month("Лютий", 28);
public static Month MARCH = new Month("Березень", 31);
@Override
public String toString() {
return "Month{" +
"name='" + name + '\''' +
", daysCount=" + daysCount +
'}';
}
}
Тут ми спростили кількість місяців до трьох замість дванадцяти, щоб приклад був коротшим.
Такі конструкції давали змогу вирішити поставлене завдання. Варіанти створюваних об'єктів обмежувалися приватним конструктором:
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
Програмісти, які використовують клас, не могли просто створювати об'єкти Month. Вони змушені були користуватися тими фінальними статичними об'єктами, які надавав розробник класу.
Це виглядало приблизно ось так:
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
Однак розробники Java звернули увагу на наявну проблему. Звісно, здорово, що програмісти спромоглися придумати її рішення за допомогою наявних у мові засобів, але воно виглядає не таким уже й простим! Необхідно було очевидне рішення, доступне навіть новачкам.
Так у Java і з'явився Enum.
По суті, Enum – це Java-клас, який надає обмежений набір значень-об'єктів.
Ось який він має вигляд:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
У визначенні ми вказали що Enum – це Java-клас, але чи дійсно це так? Так, і ми навіть можемо це перевірити.
Спробуй, наприклад, успадкувати наш enum Month від якогось іншого класу:
public abstract class AbstractMonth {
}
//помилка! No extends clause allowed to enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
Чому так відбувається? Коли ми пишемо програму:
public enum Month
компілятор перетворює цю команду на такий код:
public Class Month extends Enum
Як ти вже знаєш, множинне успадкування в Java заборонено. Тому й успадковуватися від AbstractMonth ми не змогли.
Як цю нову конструкцію, Enum, можна використовувати? І в чому її відмінність від старої конструкції зі static final полями?
Ну, наприклад, стара конструкція не дозволяла нам застосовувати свій набір значень у switch-виразах.
Уяви, що ми хочемо створити програму, яка нагадуватиме нам про те, які свята відзначаються цього місяця:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
//помилка!
case JANUARY:
}
}
}
Тут, як бачиш, компілятор викидає помилку.
Але після того, як у Java 1.5 з'явилися enum, усе стало набагато простіше:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("1 січня буде Новий рік!");
break;
case FEBRUARY:
System.out.println("2 лютого — День бабака!");
break;
case МАРТ:
System.out.println("У березні відзначається Всесвітній Жіночий День – 8 березня!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
Виведення в консоль:
1 січня буде Новий рік!
Зверни увагу: доступ до об'єктів Enum залишився статичним, як це було до Java 1.5. Нам не потрібно створювати об'єкт Month для доступу до місяців.
Під час роботи з переліками дуже важливо не забувати, що Enum – це повноцінний клас.
Це означає, що у разі потреби ти можеш визначити в ньому конструктори і методи.
Наприклад, у попередньому шматку коду ми просто вказали значення JANUARY, FEBRUARY, MARCH.
Однак ми можемо розширити наш enum Month ось таким чином:
public enum Month {
JANUARY("Січень", 31),
FEBRUARY("Лютий", 28),
MARCH("Березень", 31),
APRIL ("Квітень", 30),
MAY("Травень", 31),
JUNE("Червень", 30),
JULY ("Липень", 31),
AUGUST ("Серпень", 31),
SEPTEMBER ("Вересень", 30),
OCTOBER("Жовтень", 31),
NOVEMBER("Листопад", 30),
DECEMBER("Грудень", 31);
private String name;
private int daysCount;
Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month[] getWinterMonths() {
return new Month[]{DECEMBER, JANUARY, FEBRUARY};
}
public static Month[] getSummerMonths() {
return new Month[]{JUNE, JULY, AUGUST};
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\''' +
", daysCount=" + daysCount +
'}';
}
}
Тут ми додали до нашого enum 2 поля – назву місяця і число днів, конструктор з використанням цих полів, геттери-сеттери, метод toString(), а також 2 статичні методи.
Як бачиш, жодних проблем із цим не виникло: як ми і казали раніше, enum – це повноцінний клас:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
Виведення в консоль:
[Month{name='Червень', daysCount=30}, Month{name='Липень', daysCount=31}, Month{name='Серпень', daysCount=31}]


ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ