Hello! Сегодня мы поговорим об особом типе данных в Java —
Enum
(сокращенно от английского enumeration — «перечисление»). В чем же заключается их особенность? Давай представим, что нам нужно реализовать в программе месяцы. Казалось бы, в чем проблема? Надо просто определить, Howие свойства есть у любого месяца. Пожалуй, нам нужны прежде всего название месяца и число дней в нем. Решение задачи выглядит довольно простым:
public class Month {
private String name;
private int daysCount;
public Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
Пожалуйста, полный набор! У нас есть класс Month
, нужные поля, геттеры-сеттеры, toString()
. Разве что equals()
и hashCode()
надо добавить для полного счастья :) Однако, у нас есть концептуальная проблема. Как ты, наверное, помнишь, одно из главных преимуществ ООП заключается в том, что оно позволяет легко моделировать сущности из реального мира. Стул, машина, планета — все эти понятия из обычной жизни легко представить в программе при помощи абстракции. Проблема в том, что у некоторых сущностей в реальном мире есть строго ограниченный круг значений. В году всего 4 времени года. В музыке всего 7 нот. В календаре всего 12 месяцев. У Оушена всего 11 друзей (хотя, это спорный вопрос :)) Иными словами, обычный Java-класс не способен смоделировать эти сущности и соблюсти их естественные ограничения. В нашем классе Month
есть все нужные поля. Но ведь если его будет использовать другой программист, никто не помешает ему создавать совершенно безумные an objectы:
public class Main {
Month month1 = new Month("lolkek", 322);
Month month2 = new Month("yahoooooooooooo", 12345);
}
Если такое появится в программе, будет непросто найти виновного! С одной стороны, программист, создавший an objectы, мог бы и понять, что класс Month
подразумевает «месяц в году» и не писать подобную дичь. С другой стороны, он всего лишь пользовался теми возможностями, которые ему предоставил проектировщик класса. Можно назначать любые имена и количество дней? Он и назначил. What же в такой ситуации делать? До выхода версии языка Java 1.5 программистам приходилось, откровенно говоря, выкручиваться :) В те времена они создавали вот такие конструкции:
public class Month {
private String name;
private int daysCount;
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month JANUARY = new Month("Январь", 31);
public static Month FEBRUARY = new Month("Февраль", 28);
public static Month MARCH = new Month("Март", 31);
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
Здесь мы упростor количество месяцев до трех instead of двенадцати, чтобы пример был короче. Такие конструкции позволяли решить поставленную задачу. Варианты создаваемых an objectов ограничивались приватным конструктором:
private Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
Программисты, использующие класс, не могли просто создавать an objectы Month
. Они вынуждены были пользоваться теми финальными статическими an objectми, которые предоставлял разработчик класса. Это выглядело примерно вот так:
public class Main {
public static void main(String[] args) {
Month january = Month.JANUARY;
System.out.println(january);
}
}
Однако разработчики Java обратor внимание на существующую проблему. Конечно, здорово, что программисты смогли придумать ее решение с помощью имеющихся в языке средств, но оно выглядит не таким уж простым! Необходимо было очевидное решение, доступное даже новичкам. Так в Java и появился Enum
. По сути, Enum
— это Java-класс, который предоставляет ограниченный набор значений-an objectов. Вот How он выглядит:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
В определении мы указали что Enum
— это Java-класс, но действительно ли это так? Да, и мы даже можем это проверить. Попробуй, например, унаследовать наш enum Month
от Howого-то другого класса:
public abstract class AbstractMonth {
}
//ошибка! No extends clause allowed to enum
public enum Month extends AbstractMonth {
JANUARY,
FEBRUARY,
MARCH
}
Почему так происходит? Когда мы пишем программе:
public enum Month
компилятор преобразует эту команду в такой code:
public Class Month extends Enum
Как ты уже знаешь, множественное наследование в Java запрещено. Поэтому и наследоваться от AbstractMonth
мы не смогли. Как эту новую конструкцию, Enum
, можно использовать? И в чем ее отличие от старой конструкции со static final
полями? Ну, например, старая конструкция не позволяла нам применять свой набор значений в switch
-выражениях. Представь, что мы хотим создать программу, которая будет напоминать нам о том, Howие праздники отмечаются в этом месяце:
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
//error!
case JANUARY:
}
}
}
Здесь, How видишь, компилятор выбрасывает ошибку. Но после того, How в Java 1.5 появorсь enum
, все стало гораздо проще:
public enum Month {
JANUARY,
FEBRUARY,
MARCH
}
public class HolidayReminder {
public void printHolidays(Month month) {
switch (month) {
case JANUARY:
System.out.println("7 января будет Рождество!");
break;
case FEBRUARY:
System.out.println("В феврале празднуется День Защитника Отечества - 23 февраля!");
break;
case MARCH:
System.out.println("В марте отмечается Всемирный Женский День - 8 марта!");
break;
}
}
}
public class Main {
public static void main(String[] args) {
HolidayReminder reminder = new HolidayReminder();
reminder.printHolidays(Month.JANUARY);
}
}
Вывод в консоль:
7 января будет Рождество!
Обрати внимание: доступ к an objectм Enum
остался статическим, How это было до Java 1.5. Нам не нужно создавать an object Month
для доступа к месяцам. При работе с перечислениями очень важно не забывать, что Enum
— это полноценный класс. Это значит, что при необходимости ты можешь определить в нем конструкторы и методы. К примеру, в предыдущем куске codeа мы просто указали значения JANUARY, FEBRUARY, MARCH. Однако мы можем расширить наш enum Month
вот таким образом:
public enum Month {
JANUARY("Январь", 31),
FEBRUARY("Февраль", 28),
MARCH("Март", 31),
APRIL("Апрель", 30),
MAY("Май", 31),
JUNE("Июнь", 30),
JULY("Июль", 31),
AUGUST("Август", 31),
SEPTEMBER("Сентябрь", 30),
OCTOBER("Октябрь", 31),
NOVEMBER("Ноябрь", 30),
DECEMBER("Декабрь", 31);
private String name;
private int daysCount;
Month(String name, int daysCount) {
this.name = name;
this.daysCount = daysCount;
}
public static Month[] getWinterMonths() {
return new Month[]{DECEMBER, JANUARY, FEBRUARY};
}
public static Month[] getSummerMonths() {
return new Month[]{JUNE, JULY, AUGUST};
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDaysCount() {
return daysCount;
}
public void setDaysCount(int daysCount) {
this.daysCount = daysCount;
}
@Override
public String toString() {
return "Month{" +
"name='" + name + '\'' +
", daysCount=" + daysCount +
'}';
}
}
Здесь мы добавor к нашему enum
2 поля — название месяца и число дней, конструктор с использованием этих полей, геттеры-сеттеры, метод toString()
, а также 2 статических метода. Как видишь, ниHowих проблем с этим не возникло: How мы и сказали ранее, enum
— это полноценный класс:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(Month.getSummerMonths()));
}
}
Вывод в консоль:
[Month{name='Июнь', daysCount=30}, Month{name='Июль', daysCount=31}, Month{name='Август', daysCount=31}]
Напоследок хочу порекомендовать тебе одну крайне полезную книгу по Java, а именно — “Effective Java” Джошуа Блоха. Author — один из создателей Java, так что его советам по правильному и грамотному использованию средств языка точно можно доверять :) Применительно к нашей лекции, советую тебе обратить особое внимание на главу книги, посвященную enum
. Продуктивного тебе чтения! :)
GO TO FULL VERSION