枚举中的抽象方法
为每个枚举常量定义行为
枚举中的抽象方法 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 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));
}
}工作原理
带有方法体的每个常量都会成为枚举的一个匿名子类。
抽象方法会强制每个常量提供实现,因此编译器可以保证实现的完整性。
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,但抽象方法的方式更加安全。
如果以后添加常量,编译器会强制您为它提供方法体。而分支语句可能会悄悄遗漏新的情况。
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));
}
}抽象方法旁的具体方法
枚举可以同时包含抽象方法和具体方法。将共享逻辑放入具体方法,让变化的逻辑保留在抽象方法中。
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));
}
}策略枚举
一种改进方式是使用策略枚举:让常量委托给嵌套的策略枚举,而不是直接重写方法。
当多个常量共享相同的行为时,这种方式很有用。这里的工作日和周末采用不同的 pay 计算方式。
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"));
}
}小型 State 状态机
每个常量的独有行为在状态机中非常出色:每个状态都知道自己的转换方式。
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 方法,并在每个常量的方法体中重写它。
- 每个常量都会成为一个匿名子类。
- 编译器会强制保证实现完整性,比使用分支语句更可靠。
- 结合字段、具体默认实现和接口,构建功能丰富的枚举。
接下来将学习高性能的 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());
}
}常见问题解答
「枚举中的抽象方法」课时是免费的吗?
是的 — 「枚举中的抽象方法」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「枚举中的抽象方法」这节课中我会学到什么?
为每个枚举常量定义行为 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「枚举中的抽象方法」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。