Перечисления
Механизм перечислений был добавлен в JDK 5 версии, он позволяет удобно хранить и работать с так называемыми "категориальными данными".
Категориальные данные - это данные с ограниченным числом уникальных значений или категорий. Приведем примеры категориальных данных:
месяцы года (12 значений: январь, февраль и так далее);
пол (мужской, женский, андроген, бесполый и так далее);
вероисповедание (православие, католицизм, ислам и так далее);
день недели (понедельник, вторник, среда и так далее).
Рассмотрим пример. Для информационной системы «Электронный институт» необходимо создать класс сотрудника кафедры «DepartmentMember», в котором, среди прочих атрибутов, есть атрибут «должность» (position). С целью упрощения, предположим, что сотрудники кафедры могут занимать одну из следующих должностей:
«Инженер» (Engineer);
«Ассистент» (Assistant);
«Старший преподаватель» (Lecturer);
«Доцент» (Senior Lecturer);
«Профессор» (Professor).
Каким образом закодировать данное значение? Самый первый и очевидный вариант - просто использовать тип данных String.
public class DepartmentMember {
private String firstName;
private String lastName;
private String position;
public DepartmentMember
(String firstName, String lastName, String position) {
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
}
}
Данное решение имеет несколько очень серьезных недостатков. Вы никак не можете обеспечить корректность указания должностей и контролировать создание нового объекта класса.
public static void main(String[] args) {
DepartmentMember member1 = new DepartmentMember(
"Иван",
"Иванов",
"доцент"
);
DepartmentMember member2 = new DepartmentMember(
"Иван",
"Иванов",
"старший преподаватель"
);
}
Должность старшего преподавателя, например, может быть записана как "ст. преп.", "ст. преподаватель", "ст. пр." и так далее, не говоря про возможные орфографические ошибки. С точки зрения формальной логики, это все разные категории должностей и вы не сможете группировать или фильтровать сотрудников кафедры по полю "должность".
Другой вариант - это создать несколько статических целочисленных констант, которые можно использовать вместо строк.
class DepartmentMember {
public static final int ENGINEER = 0;
public static final int ASSISTANT = 1;
public static final int LECTURER = 2;
public static final int SENIOR_LECTURER = 3;
public static final int PROFESSOR = 4;
private String firstName;
private String lastName;
private int position;
public DepartmentMember
(String firstName, String lastName, int position) {
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
}
}
Теперь мы сможем использовать именованные константы вместо строго фиксированных строк.
public static void main(String[] args) {
DepartmentMember member1 = new DepartmentMember(
"Иван",
"Иванов",
DepartmentMember.ENGINEER
);
DepartmentMember member2 = new DepartmentMember(
"Петр",
"Петров",
DepartmentMember.PROFESSOR
);
}
Использование именованных констант позволяет отчасти избавиться от недостатков предыдущего подхода, но и этот вариант не является приемлемым. Ничто не мешает пользователю класса при создании объекта указать любое целое числа вместо именованной константы, осуществлять проверку ввода – достаточно трудоемкая задача, если в классе предусмотрено более одного категориального значения будет тяжело определять какие конкретно именованные константы необходимо использовать. К тому же, при использовании конструктора или сеттера поля «должность», нет никакого указания на необходимость использования именованных констант, программист сам должен догадаться о наличии нужных констант или читать сопроводительную документацию.
class DepartmentMember {
public static final int ENGINEER = 0;
public static final int ASSISTANT = 1;
public static final int LECTURER = 2;
public static final int SENIOR_LECTURER = 3;
public static final int PROFESSOR = 4;
public static final int FULL_TIME = 100;
public static final int PART_TIME = 101;
private String firstName;
private String lastName;
private int position;
private int contractType;
public DepartmentMember
(String firstName, String lastName, int position, int contractType) {
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
this.contractType = contractType;
}
}
public static void main(String[] args) {
DepartmentMember member1 = new DepartmentMember(
"Петр",
"Петров",
DepartmentMember.FULL_TIME, // ???
DepartmentMember.PART_TIME // ???
);
}
Подход с использованием именованных констант напоминает улучшенную версию использования механизма специальных кодов и его не следует использовать при разработке коммерческих программ.
Корректным вариантом решения данной проблемы является использование специального механизма перечислений (enumeration). По сути, перечисление – это тип, ссылочная переменная которого может принимать одно из нескольких заранее определенных значений. Реализуем класс «DepartmentMember» с помощью механизма перечислений:
class DepartmentMember {
private String firstName;
private String lastName;
private Position position;
public DepartmentMember
(String firstName, String lastName, Position position) {
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
}
public enum Position {
Engineer, Assistant, Lecturer, SeniorLecturer, Professor;
}
}
public static void main(String[] args) {
DepartmentMember member1 = new DepartmentMember(
"Иван",
"Иванов",
DepartmentMember.Position.Assistant
);
DepartmentMember member2 = new DepartmentMember(
"Петр",
"Петров",
DepartmentMember.Position.Professor
);
}
Преимущества такого подхода очевидны: поле position может принять только один из заранее определенных значений, которые указаны при создании перечислений. При вызове конструктора или сеттера поля position, пользователю класса сразу будет понятно, что необходимо передать в качестве аргумента метода.
Обратите внимание на синтаксис перечисления. Перечисление является классом, но очень своеобразным. Вместо ключевого слова class используется ключевое слово enum (от слова enumeration – перечисление). Вместо полей и методов у перечисления идет просто набор констант через запятую.
Как уже было сказано, перечисление – это класс, а это значит, что он может иметь методы, и мы можем инкапсулировать перечисление и операции работы с ним в одной оболочке.
enum Palette {
RED("#ff0000"),
YELLOW("#ffff00"),
CYAN("#00ffff"),
GREEN("#008000");
private final String color;
Palette(String color) {
this.color = color;
}
}
class Controller implements Initializable {
@FXML Button button1;
@FXML Button button2;
@FXML Button button3;
@FXML Button button4;
@Override
public void initialize(URL location, ResourceBundle resources) {
button1.setStyle("-fx-background-color: " + Palette.CYAN);
button2.setStyle("-fx-background-color: " + Palette.GREEN);
button3.setStyle("-fx-background-color: " + Palette.YELLOW);
button4.setStyle("-fx-background-color: " + Palette.RED);
}
}
В данном примере, экземпляр перечисления хранит строку с цветом.

Last updated
Was this helpful?