Пользователь Professor Hans Noodles
Professor Hans Noodles
41 уровень

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

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

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("Воскресный обед! Сегодня можно даже немного сладкого");
           case MONDAY:
               System.out.println("Обед для понедельника: куриная лапша!");
           case TUESDAY:
               System.out.println("Вторник, сегодня суп из сельдерея :(");
               //...и так далее до конца
       }
   }
}
Это одно из преимуществ Enum перед старым решением, которое применялось до Java 1.5: старое решение нельзя было использовать со switch.

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

Enum — это настоящий класс со всеми вытекающими из этого возможностями. Например, если текущей реализации дней недели тебе недостаточно, ты можешь добавить в 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.
Комментарии (102)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Никита Никитенко 10 уровень, Пермь
29 декабря 2020
спасибо, теперь понял что значения enum - это экземпляры этого класса.
Игорь 23 уровень, Минск
18 ноября 2020
В добавок, ENUM это не "настоящий класс со всеми вытекающими", нельзя создать объект такого класса
🦔 Виктор 17 уровень, Москва Expert
7 ноября 2020
Спасибо, конечно, за труд, но, получилось такое, недосказанное и примеров маловато. Школьное расписание совсем непрозрачное вышло... Исправить можно соответствующим видеоуроком Алишева. p.s. Если нужна книга, которая упомянута в статье, то пишите, поделюсь третьим изданием от 2019 года на русском языке. -- tlgrm: LetsCodeIt / SefoNotasi
Равиль Ганиев 15 уровень
7 ноября 2020
Гербер Шилдт "Java. Полное руководство." там понятно разбирается enum.
Павел 30 уровень, Новосибирск
6 ноября 2020
про name() ничего не сказали, а он есть! public final String name() Returns the name of this enum constant, exactly as declared in its enum declaration Так как name() final, он не переопределяется и всегда возвращает имя которое задекларировано. Пока toString() не переопределён, они работают одинаково.
Станислав 19 уровень, Москва
11 октября 2020
А я так хотел день недели "azaza322"
Е К 20 уровень, Краснодар
8 октября 2020
Получается разница Enum и Final только в том, что в Enum нельзя менять значения внутренних переменных?
Алексей 25 уровень, Минск
21 августа 2020
В этой статье много не досказано. После этой статьи советую посмотреть данное видео: https://www.youtube.com/watch?v=GOzNp1YAm5w&list=PLAma_mKffTOSUkXp26rgdnC0PicnmnDak&index=49 Очень толково рассказано.
Андрей Мельников 8 уровень, Львов
18 августа 2020
В switch нет ни одного break. В итоге, если сегодня воскресенье - кто-то лопнет...
Девид 0 уровень
12 августа 2020
Бедный школьник. Даже по субботам ходит в школу...