Enum в Java это специальный тип-перечисление для фиксированного набора именованных констант (например, дни недели, месяцы, статусы заказа). Он появился в Java 5 и заменил старый прием с public static-полями. Enum это полноценный класс: у него могут быть поля, конструктор и методы, а его константы удобно использовать в switch. Наследоваться от enum нельзя.

Кратко

  • Enum (перечисление) это тип для фиксированного набора именованных констант.
  • Появился в Java 5 (1.5); до него ограниченный набор задавали через приватный конструктор и public static-поля (прием из Effective Java Джошуа Блоха).
  • Enum это полноценный класс: можно добавить поля, конструктор и методы.
  • Константы enum пишут ПРОПИСНЫМИ буквами и удобно используют в switch.
  • От enum наследоваться нельзя.
  • Свои методы: values() (все константы), ordinal() (номер с нуля), valueOf() (константа по имени).
Привет! В этой статье я расскажу вам о 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 5

До появления 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 описан в книге Effective Java Джошуа Блоха
С выходом 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:
КритерийСтарый прием (public static поля)Enum (с Java 5)
Объем кодамного: конструктор и поля вручнуюкомпактно: список констант
Защита от новых значенийда, через приватный конструкторда, встроено
Работа со switchнельзяможно
Готовые методынетvalues(), ordinal(), valueOf()

Что еще нужно знать об 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. Полное описание перечислений есть в документации Oracle (Enum Types).

Читайте также

Вопросы и ответы

Что такое enum в Java?

Это специальный тип-перечисление для фиксированного набора именованных констант, например дней недели. Появился в Java 5. Enum это полноценный класс: у него могут быть поля, конструктор и методы.

Зачем нужен enum?

Чтобы ограничить множество допустимых значений. В обычный класс можно передать любое значение (например, день недели "azaza322"), а enum разрешает только заранее заданные константы.

Как объявить enum?

Ключевым словом enum и списком констант: public enum DayOfWeek { SUNDAY, MONDAY, ... }. Константы пишут прописными буквами.

Можно ли использовать enum в switch?

Да. В case пишут имя константы без имени типа, например case SUNDAY. Старый прием с public static полями так не умел.

Какие методы есть у enum?

values() возвращает массив всех констант, ordinal() порядковый номер константы (с нуля), valueOf() константу по ее имени.

Можно ли наследоваться от enum?

Нет, от перечисления наследоваться нельзя, это его ограничение. Но enum может иметь поля, конструктор, методы и реализовывать интерфейсы.