JavaRush /Java блог /Java Developer /Enum в Java: как использовать класс
Автор
Milan Vucic
Репетитор по программированию в Codementor.io

Enum в Java: как использовать класс

Статья из группы Java Developer
Привет! В этой статье я расскажу вам о Java Enums. Представь, что тебе поставили задачу: создать класс, реализующий дни недели. На первый взгляд, ничего сложного в этом нет, и твой код будет выглядеть примерно так:

public class DayOfWeek {

   private String title;

   public DayOfWeek(String title) {
       this.title = title;
   }

   public static void main(String[] args) {
       DayOfWeek dayOfWeek = new DayOfWeek("Суббота");
       System.out.println(dayOfWeek);
   }

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
И вроде бы все хорошо, но есть одна проблема: в конструктор класса DayOfWeek можно передать любой текст. Таким образом, кто-то сможет создать день недели «Лягушка», «Облачко» или «azaza322». Это явно не то поведение, которое мы ожидаем, ведь реальных дней недели существует всего 7, и у каждого из них есть название. Поэтому наша задача — как-то ограничить круг возможных значений для класса «день недели». До появления Java 1.5 разработчики были вынуждены самостоятельно придумывать решение этой проблемы, поскольку готового решения в самом языке не существовало. В те времена, если ситуация требовала ограниченного числа значений, делали так:

public class DayOfWeek {

   private String title;

   private DayOfWeek(String title) {
       this.title = title;
   }

   public static DayOfWeek SUNDAY = new DayOfWeek("Воскресенье");
   public static DayOfWeek MONDAY = new DayOfWeek("Понедельник");
   public static DayOfWeek TUESDAY = new DayOfWeek("Вторник");
   public static DayOfWeek WEDNESDAY = new DayOfWeek("Среда");
   public static DayOfWeek THURSDAY = new DayOfWeek("Четверг");
   public static DayOfWeek FRIDAY = new DayOfWeek("Пятница");
   public static DayOfWeek SATURDAY = new DayOfWeek("Суббота");

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
На что тут стоит обратить внимание:
  • Приватный конструктор. Если конструктор помечен модификатором private, объект класса нельзя создать с помощью этого конструктора. А поскольку в этом классе конструктор всего один, объект DayOfWeek нельзя создать вообще.

    
    	public class Main {
    
       		public static void main(String[] args) {
          
           			DayOfWeek sunday = new DayOfWeek();//ошибка!
       		}
    }
    
  • При этом в классе содержалось нужное количество public static объектов, которые были инициализированы нужным нам образом (названия дней правильные).

    Это позволяло использовать объекты в других классах.

    
    	public class Man {
    
       		public static void main(String[] args) {
    
           			DayOfWeek sunday = DayOfWeek.SUNDAY;
    
           			System.out.println(sunday);
      		 }
    }
    

    Вывод:

    DayOfWeek{title='Воскресенье'}

Такой подход во многом позволял решить задачу. В нашем распоряжении были 7 дней недели, и при этом никто не мог создать новые. Это решение предложил Джошуа Блох в книге «Effective Java». Книга, кстати, очень крутая и обязательна к прочтению для любого Java-разработчика.
Как использовать класс Enum - 2
С выходом Java 1.5 в языке появилось готовое решение для таких ситуаций — перечисление Enum. Enum — тоже класс. Но он специально «заточен» на решение задач, похожих на нашу: создание некоторого ограниченного круга значений. Поскольку у создателей Java уже были готовые примеры (скажем, язык С, в котором Enum уже существовал), они смогли создать оптимальный вариант.

Что такое enum?

Итак, что же из себя представляет Enum в Java? Давай посмотрим на примере того же DayOfWeek:

public enum DayOfWeek {

   SUNDAY,
   MONDAY,
   TUESDAY,
   WEDNESDAY,
   THURSDAY,
   FRIDAY,
   SATURDAY
}
Выглядит уже намного проще :) Внутри нашего Enum находятся 7 констант со статическим доступом. Мы уже можем его использовать для реализации логики в программе. Например, напишем программу, которая будет определять, нужно ли школьнику сегодня идти на учебу. У нашего школьника будет свой режим дня, обозначенный классом ScholarSchedule:

public class ScholarSchedule {

   private DayOfWeek dayOfWeek;
   //...другие поля


   public DayOfWeek getDayOfWeek() {
       return dayOfWeek;
   }

   public void setDayOfWeek(DayOfWeek dayOfWeek) {
       this.dayOfWeek = dayOfWeek;
   }
}
Переменная dayOfWeek в режиме дня определяет, какой сегодня день. А вот класс нашего школьника:

public class Scholar {

   private ScholarSchedule schedule;
   private boolean goToSchool;

   public void wakeUp() {
      
       if (this.schedule.getDayOfWeek() == DayOfWeek.SUNDAY) {
           System.out.println("Ура, можно поспать еще!");
       } else {
           System.out.println("Блин, опять в школу:(");
       }
   }
}
В методе wakeUp() при помощи Enum определяем дальнейшие действия школьника. Мы даже не описывали подробно, что значит каждая переменная в DayOfWeek, да это и не нужно: механизм дней недели и так очевиден, и если мы будем его использовать в текущем виде, любому разработчику будет понятно, что происходит в твоем коде. Еще один пример удобства Enum: его константы можно использовать с оператором switch. Например, мы пишем программу для строгой диеты, в которой блюда расписаны по дням:

public class VeryStrictDiet {
   public void takeLunch(DayOfWeek dayOfWeek) {
       switch (dayOfWeek) {
           case SUNDAY:
               System.out.println("Воскресный обед! Сегодня можно даже немного сладкого");
               break;
           case MONDAY:
               System.out.println("Обед для понедельника: куриная лапша!");
               break;
           case TUESDAY:
               System.out.println("Вторник, сегодня суп из сельдерея :(");
               break;
               //...и так далее до конца
       }
   }
}
Это одно из преимуществ Enum перед старым решением, которое применялось до Java 1.5: старое решение нельзя было использовать со switch.

Что еще нужно знать об Enum class?

Enum class — это настоящий класс со всеми вытекающими из этого возможностями. Например, если текущей реализации дней недели тебе недостаточно, ты можешь добавить в DayOfWeek переменные, конструкторы и методы:

public enum DayOfWeek {
  
   SUNDAY ("Воскресенье"),
   MONDAY ("Понедельник"),
   TUESDAY ("Вторник"),
   WEDNESDAY ("Среда"),
   THURSDAY ("Четверг"),
   FRIDAY ("Пятница"),
   SATURDAY ("Суббота");

   private String title;

   DayOfWeek(String title) {
       this.title = title;
   }

   public String getTitle() {
       return title;
   }

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
Теперь у констант нашего Enum есть поле title, геттер и переопределенный метод toString. По сравнению с обычными классами, на Enum наложили одно серьезное ограничение — от него невозможно наследоваться. Кроме того, у перечислений есть характерные только для них методы:
  • values(): возвращает массив из всех хранящихся в Enum значений:

    
    public static void main(String[] args) {
       		System.out.println(Arrays.toString(DayOfWeek.values()));
    }
    

    Вывод:

    [DayOfWeek{title='Воскресенье'}, DayOfWeek{title='Понедельник'}, DayOfWeek{title='Вторник'}, DayOfWeek{title='Среда'}, DayOfWeek{title='Четверг'}, DayOfWeek{title='Пятница'}, DayOfWeek{title='Суббота'}]

  • ordinal(): возвращает порядковый номер константы. Отсчет начинается с нуля:

    
    	public static void main(String[] args) {
    
       		int sundayIndex = DayOfWeek.SUNDAY.ordinal();
       		System.out.println(sundayIndex);
    }
    

    Вывод:

    0

  • valueOf(): возвращает объект Enum, соответствующий переданному имени:

    
    public static void main(String[] args) {
       DayOfWeek sunday = DayOfWeek.valueOf("SUNDAY");
       System.out.println(sunday);
    }
    

    Вывод:

    DayOfWeek{title='Воскресенье'}

Обрати внимание: мы указываем названия элементов Enum прописными буквами, поскольку это константы, а для них предусмотрена именно такая запись, а не camelCase.
Комментарии (146)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
JavaRusher853 Уровень 23
16 февраля 2024
enum - Это довольно непростая тема для меня, может вы ребята, порекомендуете какие-либо полезные ссылки для изучения этого класса?)
Максим Li Уровень 36
6 декабря 2023
Хорошая статья
Alexander Rozenberg Уровень 32
18 июля 2023
fine
No Name Уровень 32
11 июня 2023
+ статья в копилку
Ислам Уровень 33
31 мая 2023
Nice
Vitya Уровень 33
11 апреля 2023
* Информация для продвинутых юзеров. На самом деле возможно ограниченное наследование от Enum. Подробнее здесь: https://kibungo.livejournal.com/19999.html Пример наследования и переопределения общего метода:

public enum MyEnum{
    VAL1 {
        public int returnMyVal(){
            return 20;
        }
    },
    VAL2;
    
    public int returnMyVal(){ return 10; }
}
------------------------
System.out.println(MyEnum.VAL1.returnMyVal()) // 20
System.out.println(MyEnum.VAL2.returnMyVal()) // 10
partiec Уровень 33
7 марта 2023
Есть ли какой-то смысл в таком заковыристом переопределении toString()?
Dina Goncharova Уровень 39
21 августа 2022
Хорошая статья. Не теряет своей актуальности. По ней или учишь, или повторяешь. )
Олег Есть Уровень 1
1 июля 2022
"По сравнению с обычными классами, на Enum наложили одно серьезное ограничение — от него невозможно наследоваться." Что за бред вы пишете?! Все перечисления изначально неявно расширяют класс java.lang.Enum, а поскольку в Java нету множественного наследования, то и расширять enum уже больше ничего не может!!!
Жора Нет Уровень 39
13 мая 2022
В статье enum в коде с маленькой буквы, в комментариях к коду - Enum с большой Почему не говорится, что это разные вещи? Если у нас несколько enum`ов с перечислениями в приложении и нам нужно передать один из них как аргумент в метод можно сделать просто так someMethod(Enum e) { и здесь написать условие для выбора одного из enum`ов } Т.е. не нужно приводить все enum`ы к единому интерфейсу или абстрактному классу, чтобы выбрать один из enum`ов Как я понял Enum(тот который с большой буквы) - это что-то типа пустого интерфейса для всех enum`ов