Абстрактные методы в перечислениях
Задавайте поведение для каждой константы
«Абстрактные методы в перечислениях» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Поведение для каждой константы
Иногда каждой константе перечисления требуется собственное поведение, а не только собственные данные.
Java позволяет объявить в перечислении метод abstract и переопределить его для каждой константы. Это шаблон тела метода, специфичного для константы.
public class Main {
enum Operation {
PLUS { public int apply(int a, int b) { return a + b; } },
MINUS { public int apply(int a, int b) { return a - b; } };
public abstract int apply(int a, int b);
}
public static void main(String[] args) {
System.out.println(Operation.PLUS.apply(3, 4));
System.out.println(Operation.MINUS.apply(10, 6));
}
}Как это работает
Каждая константа с телом становится анонимным подклассом перечисления.
Метод abstract требует, чтобы каждая константа предоставила реализацию, поэтому компилятор гарантирует полноту.
public class Main {
enum Operation {
TIMES { public int apply(int a, int b) { return a * b; } };
public abstract int apply(int a, int b);
}
public static void main(String[] args) {
// The constant is an instance of a generated subclass
System.out.println(Operation.TIMES.getClass().getSimpleName().isEmpty()
? "anonymous subclass" : Operation.TIMES.apply(6, 7));
}
}Полноценный калькулятор
Четыре арифметические операции представлены константами, и у каждой есть собственное тело apply.
public class Main {
enum Operation {
PLUS { public double apply(double a, double b) { return a + b; } },
MINUS { public double apply(double a, double b) { return a - b; } },
TIMES { public double apply(double a, double b) { return a * b; } },
DIVIDE{ public double apply(double a, double b) { return a / b; } };
public abstract double apply(double a, double b);
}
public static void main(String[] args) {
double x = 6, y = 3;
for (Operation op : Operation.values()) {
System.out.printf("%s -> %.1f%n", op, op.apply(x, y));
}
}
}Объединение полей и поведения
Вы можете объединить поле (символ) с поведением, специфичным для каждой константы. Поле обозначает операцию, а метод выполняет её.
public class Main {
enum Operation {
PLUS("+") { public double apply(double a, double b) { return a + b; } },
MINUS("-") { public double apply(double a, double b) { return a - b; } };
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
public abstract double apply(double a, double b);
@Override public String toString() { return symbol; }
}
public static void main(String[] args) {
Operation op = Operation.PLUS;
System.out.println("2 " + op + " 3 = " + op.apply(2, 3));
}
}Почему бы не использовать оператор выбора
Можно реализовать apply с помощью одного оператора выбора по константе, но подход с методом abstract безопаснее.
Если позднее добавить константу, компилятор заставит Вас предоставить для неё тело. Оператор выбора может незаметно не учитывать новый вариант.
public class Main {
enum Op {
SQUARE { public int apply(int n) { return n * n; } },
CUBE { public int apply(int n) { return n * n * n; } };
public abstract int apply(int n);
}
public static void main(String[] args) {
System.out.println(Op.SQUARE.apply(5));
System.out.println(Op.CUBE.apply(3));
}
}Конкретные методы и методы abstract
В перечислении можно сочетать методы abstract и конкретные методы. Общую логику следует помещать в конкретный метод, а изменяющуюся оставлять в методе abstract.
public class Main {
enum Shape {
CIRCLE { public double area(double x) { return Math.PI * x * x; } },
SQUARE { public double area(double x) { return x * x; } };
public abstract double area(double x);
public String report(double x) { return this + " area = " + area(x); }
}
public static void main(String[] args) {
System.out.println(Shape.CIRCLE.report(2));
}
}Перечисления-стратегии
Усовершенствованный вариант — перечисление-стратегия: константы делегируют выполнение вложенному перечислению-стратегии, вместо того чтобы переопределять поведение напрямую.
Это удобно, когда несколько констант используют одинаковое поведение. В данном случае будни и выходные отличаются расчётом оплаты.
public class Main {
enum PayType {
WEEKDAY { int overtime(int mins) { return mins; } },
WEEKEND { int overtime(int mins) { return mins * 2; } };
abstract int overtime(int mins);
int pay(int mins) { return mins + overtime(Math.max(0, mins - 480)); }
}
public static void main(String[] args) {
System.out.println("Weekend pay: " + PayType.WEEKEND.pay(540));
}
}Перечисление с интерфейсом и телами методов
Сочетайте интерфейс с телами, специфичными для констант, чтобы добиться максимальной полиморфности.
public class Main {
interface Command { String run(); }
enum Action implements Command {
START { public String run() { return "starting..."; } },
STOP { public String run() { return "stopping..."; } };
}
public static void main(String[] args) {
for (Action a : Action.values()) System.out.println(a.run());
}
}Доступ к полям из тел методов
Тело, специфичное для константы, может обращаться к её собственным полям, поскольку каждое такое тело выполняется в контексте соответствующего экземпляра.
public class Main {
enum Tax {
STANDARD(0.20) { double apply(double amt) { return amt * (1 + rate); } },
REDUCED(0.05) { double apply(double amt) { return amt * (1 + rate); } };
protected final double rate;
Tax(double rate) { this.rate = rate; }
abstract double apply(double amt);
}
public static void main(String[] args) {
System.out.printf("%.2f%n", Tax.STANDARD.apply(100));
}
}Значение по умолчанию и переопределение
Вы можете задать конкретную реализацию по умолчанию в теле перечисления и переопределить её только для отличающихся констант.
public class Main {
enum Notifier {
EMAIL,
SMS { String send(String m) { return "SMS: " + m; } };
String send(String m) { return "EMAIL: " + m; }
}
public static void main(String[] args) {
System.out.println(Notifier.EMAIL.send("hi"));
System.out.println(Notifier.SMS.send("hi"));
}
}Миниатюрный автомат состояний
Поведение для каждой константы особенно полезно в автоматах состояний: каждое состояние знает собственный переход.
public class Main {
enum State {
IDLE { State next() { return RUNNING; } },
RUNNING { State next() { return DONE; } },
DONE { State next() { return DONE; } };
abstract State next();
}
public static void main(String[] args) {
State s = State.IDLE;
for (int i = 0; i < 3; i++) { System.out.println(s); s = s.next(); }
System.out.println(s);
}
}Быстрая проверка
Проверьте, насколько хорошо Вы поняли методы abstract в перечислениях.
Итоги
Вы изучили поведение для каждой константы:
- Объявляйте метод abstract и переопределяйте его в теле каждой константы.
- Каждая константа становится анонимным подклассом.
- Компилятор обеспечивает полноту, что надёжнее оператора выбора.
- Сочетайте поля, конкретные реализации по умолчанию и интерфейсы для создания функциональных перечислений.
Далее — высокопроизводительный EnumSet.
public class Main {
enum E { A { int v() { return 1; } }; abstract int v(); }
public static void main(String[] args) {
System.out.println("Abstract enum recap: " + E.A.v());
}
}Часто задаваемые вопросы
Урок «Абстрактные методы в перечислениях» бесплатный?
Да — полный текст урока «Абстрактные методы в перечислениях» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Абстрактные методы в перечислениях»?
Задавайте поведение для каждой константы Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Абстрактные методы в перечислениях»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Перечисления с полями и методами
- Абстрактные методы в перечислениях
- EnumSet
- EnumMap