🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
<!-- Constant-Specific Methods --> ## 常量特定方法 Java 的 enum 有一个非常有趣的特性,即它允许程序员为 enum 实例编写方法,从而为每个 enum 实例赋予各自不同的行为。要实现常量相关的方法,你需要为 enum 定义一个或多个 abstract 方法,然后为每个 enum 实例实现该抽象方法。参考下面的例子: ```java // enums/ConstantSpecificMethod.java import java.util.*; import java.text.*; public enum ConstantSpecificMethod { DATE_TIME { @Override String getInfo() { return DateFormat.getDateInstance() .format(new Date()); } }, CLASSPATH { @Override String getInfo() { return System.getenv("CLASSPATH"); } }, VERSION { @Override String getInfo() { return System.getProperty("java.version"); } }; abstract String getInfo(); public static void main(String[] args) { for(ConstantSpecificMethod csm : values()) System.out.println(csm.getInfo()); } } ``` 输出为: ```java May 9, 2017 C:\Users\Bruce\Documents\GitHub\on- java\ExtractedExamples\\gradle\wrapper\gradle- wrapper.jar 1.8.0_112 ``` 通过相应的 enum 实例,我们可以调用其上的方法。这通常也称为表驱动的代码(table-driven code,请注意它与前面提到的命令模式的相似之处)。 在面向对象的程序设计中,不同的行为与不同的类关联。而通过常量相关的方法,每个 enum 实例可以具备自己独特的行为,这似乎说明每个 enum 实例就像一个独特的类。在上面的例子中,enum 实例似乎被当作其“超类”ConstantSpecificMethod 来使用,在调用 getInfo() 方法时,体现出多态的行为。 然而,enum 实例与类的相似之处也仅限于此了。我们并不能真的将 enum 实例作为一个类型来使用: ```java // enums/NotClasses.java // {javap -c LikeClasses} enum LikeClasses { WINKEN { @Override void behavior() { System.out.println("Behavior1"); } }, BLINKEN { @Override void behavior() { System.out.println("Behavior2"); } }, NOD { @Override void behavior() { System.out.println("Behavior3"); } }; abstract void behavior(); } public class NotClasses { // void f1(LikeClasses.WINKEN instance) {} // Nope } ``` 输出为(前 12 行): ``` Compiled from "NotClasses.java" abstract class LikeClasses extends java.lang.Enum<LikeClasses> { public static final LikeClasses WINKEN; public static final LikeClasses BLINKEN; public static final LikeClasses NOD; public static LikeClasses[] values(); Code: 0: getstatic #2 // Field $VALUES:[LLikeClasses; 3: invokevirtual #3 // Method "[LLikeClasses;".clone:()Ljava/lang/Object; ... ``` 在方法 f1() 中,编译器不允许我们将一个 enum 实例当作 class 类型。如果我们分析一下编译器生成的代码,就知道这种行为也是很正常的。因为每个 enum 元素都是一个 LikeClasses 类型的 static final 实例。 同时,由于它们是 static 实例,无法访问外部类的非 static 元素或方法,所以对于内部的 enum 的实例而言,其行为与一般的内部类并不相同。 再看一个更有趣的关于洗车的例子。每个顾客在洗车时,都有一个选择菜单,每个选择对应一个不同的动作。可以将一个常量相关的方法关联到一个选择上,再使用一个 EnumSet 来保存客户的选择: ```java // enums/CarWash.java import java.util.*; public class CarWash { public enum Cycle { UNDERBODY { @Override void action() { System.out.println("Spraying the underbody"); } }, WHEELWASH { @Override void action() { System.out.println("Washing the wheels"); } }, PREWASH { @Override void action() { System.out.println("Loosening the dirt"); } }, BASIC { @Override void action() { System.out.println("The basic wash"); } }, HOTWAX { @Override void action() { System.out.println("Applying hot wax"); } }, RINSE { @Override void action() { System.out.println("Rinsing"); } }, BLOWDRY { @Override void action() { System.out.println("Blowing dry"); } }; abstract void action(); } EnumSet<Cycle> cycles = EnumSet.of(Cycle.BASIC, Cycle.RINSE); public void add(Cycle cycle) { cycles.add(cycle); } public void washCar() { for(Cycle c : cycles) c.action(); } @Override public String toString() { return cycles.toString(); } public static void main(String[] args) { CarWash wash = new CarWash(); System.out.println(wash); wash.washCar(); // Order of addition is unimportant: wash.add(Cycle.BLOWDRY); wash.add(Cycle.BLOWDRY); // Duplicates ignored wash.add(Cycle.RINSE); wash.add(Cycle.HOTWAX); System.out.println(wash); wash.washCar(); } } ``` 输出为: ``` [BASIC, RINSE] The basic wash Rinsing [BASIC, HOTWAX, RINSE, BLOWDRY] The basic wash Applying hot wax Rinsing Blowing dry ``` 与使用匿名内部类相比较,定义常量相关方法的语法更高效、简洁。 这个例子也展示了 EnumSet 了一些特性。因为它是一个集合,所以对于同一个元素而言,只能出现一次,因此对同一个参数重复地调用 add0 方法会被忽略掉(这是正确的行为,因为一个 bit 位开关只能“打开”一次),同样地,向 EnumSet 添加 enum 实例的顺序并不重要,因为其输出的次序决定于 enum 实例定义时的次序。 除了实现 abstract 方法以外,程序员是否可以覆盖常量相关的方法呢?答案是肯定的,参考下面的程序: ```java // enums/OverrideConstantSpecific.java public enum OverrideConstantSpecific { NUT, BOLT, WASHER { @Override void f() { System.out.println("Overridden method"); } }; void f() { System.out.println("default behavior"); } public static void main(String[] args) { for(OverrideConstantSpecific ocs : values()) { System.out.print(ocs + ": "); ocs.f(); } } } ``` 输出为: ``` NUT: default behavior BOLT: default behavior WASHER: Overridden method ``` 虽然 enum 有某些限制,但是一般而言,我们还是可以将其看作是类。