枚举类型都是final的,不可以被继承扩展,但是枚举类型可以实现接口,因此可以将通用方法创建一个接口,然后让枚举类型实现接口,例如以操作码为例,在接口Operation中定义了apply方法的参数以及返回类型,让BasicOperation实现这个接口

    1. // Emulated extensible enum using an interface
    2. public interface Operation {
    3. double apply(double x, double y);
    4. }
    5. public enum BasicOperation implements Operation {
    6. PLUS("+") {
    7. public double apply(double x, double y) { return x + y; }
    8. },
    9. MINUS("-") {
    10. public double apply(double x, double y) { return x - y; }
    11. },
    12. TIMES("*") {
    13. public double apply(double x, double y) { return x * y; }
    14. },
    15. DIVIDE("/") {
    16. public double apply(double x, double y) { return x / y; }
    17. };
    18. private final String symbol;
    19. BasicOperation(String symbol) {
    20. this.symbol = symbol;
    21. }
    22. @Override public String toString() {
    23. return symbol;
    24. }
    25. }

    虽然枚举类型(BasicOperation)不可扩展,但接口类型(Operation)是可以扩展的,并且它是用于表示API中的操作的接口类型。 你可以定义另一个实现此接口的枚举类型,并使用此新类型的实例来代替基本类型。 例如,假设想要定义前面所示的操作类型的扩展,包括指数运算和余数运算。 你所要做的就是编写一个实现Operation接口的枚举类型:

    1. // Emulated extension enum
    2. public enum ExtendedOperation implements Operation {
    3. EXP("^") {
    4. public double apply(double x, double y) {
    5. return Math.pow(x, y);
    6. }
    7. },
    8. REMAINDER("%") {
    9. public double apply(double x, double y) {
    10. return x % y;
    11. }
    12. };
    13. private final String symbol;
    14. ExtendedOperation(String symbol) {
    15. this.symbol = symbol;
    16. }
    17. @Override public String toString() {
    18. return symbol;
    19. }
    20. }

    只要API编写为接口类型(Operation),而不是实现(BasicOperation),现在就可以在任何可以使用基本操作的地方使用新操作
    不仅可以在任何需要“基本枚举”的地方传递“扩展枚举”的单个实例,而且还可以传入整个扩展枚举类型,并使用其元素。 例如,它执行之前定义的所有扩展操作:

    1. public static void main(String[] args) {
    2. double x = Double.parseDouble(args[0]);
    3. double y = Double.parseDouble(args[1]);
    4. test(ExtendedOperation.class, x, y);
    5. }
    6. private static <T extends Enum<T> & Operation> void test(
    7. Class<T> opEnumType, double x, double y) {
    8. for (Operation op : opEnumType.getEnumConstants())
    9. System.out.printf("%f %s %f = %f%n",
    10. x, op, y, op.apply(x, y));
    11. }

    注意,扩展的操作类型的类字面文字(ExtendedOperation.class)从main方法里传递给了test方法,用来描述扩展操作的集合。这个类的字面文字用作限定的类型令牌(条目 33)。opEnumType参数中复杂的声明(<T extends Enum<T> & Operation> Class<T>)确保了Class对象既是枚举又是Operation的子类,这正是遍历元素和执行每个元素相关联的操作时所需要的。
    第二种方式是传递一个Collection<? extends Operation>,这是一个限定通配符类型(条目 31),而不是传递了一个class对象:

    1. public static void main(String[] args) {
    2. double x = Double.parseDouble(args[0]);
    3. double y = Double.parseDouble(args[1]);
    4. test(Arrays.asList(ExtendedOperation.values()), x, y);
    5. }
    6. private static void test(Collection<? extends Operation> opSet,
    7. double x, double y) {
    8. for (Operation op : opSet)
    9. System.out.printf("%f %s %f = %f%n",
    10. x, op, y, op.apply(x, y));
    11. }

    生成的代码稍微不那么复杂,test方法灵活一点:它允许调用者将多个实现类型的操作组合在一起。另一方面,也放弃了在指定操作上使用EnumSet(条目 36)和EnumMap(条目 37)的能力。
    上面的两个程序在运行命令行输入参数4和2时生成以下输出:

    4.000000 ^ 2.000000 = 16.000000
    4.000000 % 2.000000 = 0.000000
    

    使用接口来模拟可扩展枚举的一个小缺点是,实现不能从一个枚举类型继承到另一个枚举类型。如果实现代码不依赖于任何状态,则可以使用默认实现(条目 20)将其放置在接口中。在我们的Operation示例中,存储和检索与操作关联的符号的逻辑必须在BasicOperationExtendedOperation中重复。在这种情况下,这并不重要,因为很少的代码是冗余的。如果有更多的共享功能,可以将其封装在辅助类或静态辅助方法中,以消除代码冗余。
    该条目中描述的模式在Java类库中有所使用。例如,java.nio.file.LinkOption枚举类型实现了CopyOptionOpenOption接口。
    总之,虽然不能编写可扩展的枚举类型,但是你可以编写一个接口来配合实现接口的基本的枚举类型,来对它进行模拟。这允许客户端编写自己的枚举(或其它类型)来实现接口。如果API是根据接口编写的,那么在任何使用基本枚举类型实例的地方,都可以使用这些枚举类型实例。