Иногда нужно описать ограниченное множество элементов. Встарь это делали посредством целочисленных констант:
public final class HairColourInt {
// не позволим никому создавать экземпляры
private HairColourInt() {}
public static final int BLONDE = 0;
public static final int FAIR = 1;
public static final int RED = 2;
public static final int BROWN = 3;
public static final int BLACK = 4;
}
Минусов у такого подхода несколько:
- Можно, добавляя в новую константу, по ошибке использовать старое числовое значение, например, добавить GREEN = 4, в то время как BLACK тоже = 4.
- int приводится к строке как число, это неудобно для отладки.
- В метод, который ожидает на вход значение такой константы, можно передать произвольное число. Например, здесь мы используем числа от нуля до четырёх, но можно передать хоть -1, хоть 2_000_000_000.
Enum
enum — это вид класса. У него есть ограниченный набор элементов, и все они доступны статически.
Другие названия: enumerated type, перечисляемый тип, перечисление.
public enum HairColour {
BLONDE, FAIR, RED, BROWN, BLACK
}
Если объявить метод, скажем, findPersonWith(HairColour colour), то все значения, которые можно передать в метод, ограничиваются следующими: BLONDE, FAIR, RED, BROWN, BLACK и null.
Person redhead = findPersonWith(HairColour.RED);
Стандартные методы enum'ов
Все enum'ы являются наследниками класса java.lang.Enum и обладают следующими instance-методами:
- name()
- Имя константы. HairColour.BROWN.name() == "BROWN"
- ordinal()
- Порядковый номер. HairColor.BLONDE.ordinal() == 0
Также компилятор генерирует для каждого enum-класса следующие статические методы:
- valueOf(String name)
- Находит константу по имени. HairColour.valueOf("BROWN") == HairColour.BROWN; HairColour.valueOf("YELLOW") бросит IllegalArgumentException, т. к. константы с таким именем нет
- values()
- Массив допустимых значений. HairColor.values() == [BLONDE, FAIR, RED, BROWN, BLACK]
Стоит учитывать, что, т. к. в Java массивы изменяемы, метод values() каждый раз возвращает новую копию массива (производит защитное копирование). Подробнее об экономии памяти в этой ситуации — в статье «Enum в Java. Маленькая недоработка».
Добавление собственных полей и методов
Очень часто нужно добавить какую-нибудь информацию к элементам enum'а. Это можно сделать так:
public enum HairColour {
// Пусть у каждого цвета волос
// будет степень яркости.
BLONDE(1),
FAIR(.8f),
RED(.7f),
BROWN(.4f),
BLACK(0);
// Поле для хранения данных.
// Должно быть финальным:
// недопустимо, чтобы у элемента перечисления
// было состояние, т. к. он — константа.
private final float brightness;
// Конструктор. Он приватный,
// изменить это нельзя.
HairColour(float brightness) {
this.brightness = brightness;
}
// геттер для поля
public float getBrightness() {
return brightness;
}
// Метод для поиска цвета
// по уровню яркости.
public static HairColour withBrightness(float brightness) {
// обходим все возможные значения
for (HairColour colour : values()) {
// если у очередного цвета
// подходящий уровень яркости — возвращаем
if (colour.brightness == brightness) {
return colour;
}
}
// если нужный цвет не найден —
// выбрасываем исключение
throw new NoSuchElementException(
"There's no HairColour with brightness " + brightness);
}
}
В этом примере HairColour.RED.getBrightness() == .7f, HairColour.withBrightness(0) == HairColour.BLACK, а HairColour.withBrightness(.003f) бросит исключение NoSuchElementException.
Реализация интерфейсов
Enum'ы могут реализовывать интерфейсы.
public enum HairColour implements Runnable {
...
@Override
public void run() {
System.out.println("person with " + name().toLowerCase() + " hair runs");
}
}
HairColour.FAIR.run() выведет person with fair hair runs.
Собственные методы элементов enum'а
Отдельные элементы перечисления могут быть анонимными классами и иметь собственные методы. Например:
public enum HairColour implements Runnable {
BLONDE(1),
FAIR(.8f),
RED(.7f) { // тело анонимного класса
@Override
public boolean hasSoul() {
return false;
}
},
BROWN(.4f),
BLACK(0);
...
public boolean hasSoul() {
return true;
}
}
Таким образом, enum — это не просто набор констант, это почти полноценный класс и очень мощный инструмент.