0Pricing
Java Academy · 课时

枚举中的抽象方法

为每个枚举常量定义行为

枚举中的抽象方法 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 带字段和方法的枚举
  2. 枚举中的抽象方法
  3. EnumSet
  4. EnumMap
← 返回 Java Academy