1. Предыстория появления enum

Сегодня мы изучим еще одну разновидность типов данных в Java — enum. Название enum происходит от слова Enumeration — перечисление. Что же это за тип данных и с чем его едят?

Иногда в работе программиста возникает необходимость создать новый тип данных, переменные которого принимают только значения из небольшого фиксированного списка.

Например, тип ДеньНедели может принимать только значения ПОНЕДЕЛЬНИК, ВТОРНИК, СРЕДА, ... Всего 7 значений. Или тип Месяц может принимать только значения ЯНВАРЬ, ФЕВРАЛЬ, МАРТ, ... Всего 12 значений.

Можно, конечно, использовать числа (тип int): 1 — понедельник, 2 — вторник и т.д. Но кто-то случайно может присвоить такой переменной значение 8, или, например, значение 0.

Легко может возникнуть ситуация, когда один программист думает, что дни недели (или месяцы) нумеруются с нуля, другой — что с единицы и т.д.

Поэтому в Java придумали тип данных, который состоит из конечного набора значенийenum.


2. Объявление типа

Выглядит объявление нового enum типа данных так:

enum ИмяТипа
{
   ЗНАЧЕНИЕ1,
   ЗНАЧЕНИЕ2,
   ЗНАЧЕНИЕ3
}

Где ИмяТипа — это имя нового типа (класса), а в скобках через запятую перечислены возможные значения: Значение1, Значение2, Значение3.

Давайте ради примера создадим свой enum для типа ДеньНедели:

Код Примечание
enum Day
{
   MONDAY,
   TUESDAY,
   WEDNESDAY,
   THURSDAY,
   FRIDAY,
   SATURDAY,
   SUNDAY
}
Новый тип Day

Понедельник
Вторник
Среда
Четверг
Пятница
Суббота
Воскресенье

Присваивать значения переменным этого типа можно таким образом:

Day day = Day.MONDAY;

Пример:

Код Примечание
Day day = Day.FRIDAY;
System.out.println(day);
На экран будет выведено:
FRIDAY


3. Методы enum’а

Тип enum имеет несколько встроенных методов, два из них очень интересные:

Статический метод values() возвращает массив всех значений типа enum:

Код Примечание
Day[] days = Day.values();

for (Day day: days)
   System.out.println(day);







System.out.println(days[2]);
В переменную days сохраняется массив значений типа Day (7 штук)

Выводим содержимое массива на экран:
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY

WEDNESDAY

Метод ordinal() возвращает порядковый номер константы. Вызывать его нужно не у класса enum, а у значения enum:

Код Вывод на экран
System.out.println(Day.MONDAY.ordinal());
System.out.println(Day.FRIDAY.ordinal());
System.out.println(Day.SUNDAY.ordinal());
0
4
6


4. Преобразование в класс

На самом деле никакой магии тут нет, и компилятор просто подсыпал нам немного синтаксического сахара. Во время компиляции класс enum Day будет преобразован в обычный класс:

Код, упрощенная версия Примечание
public class Day
{
   public static final Day MONDAY = new Day(0);
   public static final Day TUESDAY = new Day(1);
   public static final Day WEDNESDAY = new Day(2);
   public static final Day THURSDAY = new Day(3);
   public static final Day FRIDAY = new Day(4);
   public static final Day SATURDAY = new Day(5);
   public static final Day SUNDAY = new Day(6);

    private static final Day[] array = {MONDAY, TUESDAY,
      WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};

   private final int value;

   private Day (int value)
   {
      this.value = value;
   }

   public int ordinal()
   {
      return this.value;
   }

   public static Day[] values()
   {
      return array;
   }
}
Класс Day

Список статических констант-значений







Массив со всеми значениями типа Day


Переменная со значением конкретного объекта Day

private конструктор класса Day — объекты класса Day можно создавать только внутри класса Day.



Метод ordinal нужно вызвать у объекта Day.

Он возвращает значение объекта — value.


Метод возвращает статический массив со всеми значениями класса Day

Если убрать из класса Day все статические методы и переменные, мы получим такую картину:

Код Примечание
public class Day
{
  private int value;

  private Day (int value)
  {
    this.value = value;
  }

  public int ordinal()
  {
    return this.value;
  }
}


Переменная value хранит значение объекта Day

Объекты Day можно создавать только внутри класса Day — конструктор private.




Метод ordinal() возвращает значение value объекта Day.

Т.е. в принципе ничего страшного тут не происходит. Компилятор создает класс Day, затем добавляет в него константы, которые были значениями у enum, добавляет недостающие методы и делает конструктор класса private. Как работают конструкторы, мы разберем немного позднее.

Теперь, думаем, понятно, почему мы заносим значение в переменную таким образом:

Day day = Day.MONDAY;

MONDAY — это просто статическое поле (константа) класса Day. А при обращении к статическим методам и полям извне класса перед именем поля или метода нужно указывать имя класса.



5. Еще методы enum’а

Еще у всех enum-классов есть несколько интересных особенностей.

Преобразование в строку и обратно

Чтобы преобразовать объект типа enum в строку, у него нужно вызвать метод toString().

String str = Day.MONDAY.toString();

Для обратного преобразования (строки в объект Day) можно воспользоваться статическим методом valueOf():

Day day = Day.valueOf("MONDAY");

Очень удобно и полезно во многих случаях.

Преобразование в число и обратно

Как преобразовать объект типа enum в число, вы уже знаете: для этого нужно вызвать метод ordinal():

int index = Day.MONDAY.ordinal();

Для обратного преобразования (числа в объект Day) нужно воспользоваться конструкцией подлиннее:

Day day = Day.values()[2];

Примеры:

Код Примечание
Day day = Day.MONDAY;
int index = day.ordinal();
Day newDay = Day.values()[index+2];
Понедельник
Получаем индекс понедельника (0)
День недели на 2 дня позже понедельника

Важный момент, т.к. значения типа enum представляют собой фиксированный набор констант, их можно сравнивать через ==. Ну не может существовать двух одинаковых объектов MONDAY с разными ссылками. Каждый объект-значение типа enum существует только в единственном экземпляре. Поэтому сравнение переменных типа enum через == всегда будет работать.



6. Добавление своих методов в enum

Так как enum во время компиляции превращается в обычный класс, в нем можно объявлять методы: эти методы просто добавятся в класс, который сгенерирует компилятор. Например, мы хотим, чтобы наш enum Day возвращал не массив значений, а список.

Тогда в него можно дописать такой код:

Код Примечание
enum Day
{
   MONDAY,
   TUESDAY,
   WEDNESDAY,
   THURSDAY,
   FRIDAY,
   SATURDAY,
   SUNDAY;

   public static List<Day> asList()
   {
      ArrayList<Day> list = new ArrayList<Day>();

      Collections.addAll(list, values());

      return list;
   }

}








После списка значений нужно поставить точку с запятой.



Создаем объект ArrayList.

Добавляем в него значения из массива: его возвращает метод values().
Возвращаем список.

Теперь этот метод можно вызывать в коде:

Код Примечание
List<Day> list = Day.asList();
В переменную list будет сохранен список всех значений enum’а Day.