1. 在J2SE5.0中要定义枚举类型是使用enum关键词,枚举类型主要提供一些常数。如下列代码定义了Action枚举类型:
public enum Action { TURN_LEFT, TURN_RIGHT, SHOOT }
在Action.java中编写此段代码并编译,虽然语法上不像是在定义类,但枚举类型本质上就是一个类。所以编译完成后,会产生一个Action.class文件。
下面的代码是说明如何使用定义好的枚举类型一个示例:
public class EnumDemo { public static void main(String[] args) { doAction(Action.TURN_RIGHT); } public static void doAction(Action action) { switch(action) { case TURN_LEFT: System.out.println("向左转"); break; case TURN_RIGHT: System.out.println("向右转"); break; case SHOOT: System.out.println("射击"); break; } } }
运行结果:向右转。其中doAction()方法所接受的变量必须是Action枚举类型,如果对此方法输入其他类型的变量,编译器会报告错误。另外,如果在上面的switch中加入了不属于Action中枚举的值,编译器也会报告错误。例如:在上面代码case SHOOT段下面再加上以下代码:
case STOP:
System.out.println("STOP");
break;
则在编译时编译器会显示以下错误:
unqualified enumeration constant name required
case STOP:
^
2. 可以在一个独立的文件中声明枚举值,或是在某个类中声明枚举成员。例如:
public class EnumDemo2 { private enum InnerAction {TURN_LEFT,TURN_RIGHT,SHOOT}; public static void main(String[] args) { doAction(InnerAction.TURN_RIGHT); } public static void doAction(InnerAction action) { switch(action) { case TURN_LEFT: System.out.println("向左转"); break; case TURN_RIGHT: System.out.println("向右转"); break; case SHOOT: System.out.println("射击"); break; } } }
由于枚举类型本质上还是类,所以这段代码中枚举声明方式有些像在声明内嵌类。在编译完EnumDemo2.java后,会有一些额外的 .class文件产生,在此例中就是EnumDemo2$InnerAction.class与EnumDemo2$1.class。看到这两个文件,就可以知道实际上编译器产生了成员内部类和匿名内部类。
上面通过枚举类型设定常数的方式比旧版本的常数设定方式多了编译时期类型检查的好处。以下将深入讨论枚举类型的一些知识,以便深入理解。
2. 深入枚举类型:
定义枚举类型其实就是在定义一个类,只不过很多细节由编译器帮你补齐了,所以,某种程度上enum关键词的作用就像是class或interface.
当使用enum定义枚举类型时,实际上所定义出来的类型是继承自java.lang.Enum类。而每个被枚举的成员其实就是定义的枚举类型的一个实例,它们都被默认为final。无法改变常数名称所设定的值,它们也是public和static的成员,这与接口中的常量限制相同。可以通过类名称直接使用它们。
如1中所定义的枚举类型Action,TURN_LEFT,TURN_RIGHT,SHOOT都是Action的一个对象实例。因为是对象,所以,对象上自然有一些方法可以调用。如从Object继承焉的toString()方法被重新定义了,可以让你直接取得枚举值的字符串描述;values()方法可以让您取得所有的枚举成员实例,并以数组方式返回。您可以使用这两个方法来简单的将Action的枚举成员显示出来。静态valueOf()方法可以让您将指定的字符串尝试转换为枚举类型。可以用compareTo()方法来比较两个枚举对象在枚举时的顺序。-1之前,0位置相同,1之后。对于每个枚举成员,使用ordinal()方法,依枚举顺序得到位置索引,默认以0开始。
3.枚举上的方法:定义枚举类型基本上就是在定义类,定义枚举类型时也可以定义方法。如可以为枚举加上一些描述,而不是使用默认的toString()返回值来描述枚举值。如下代码所示:
public enum DetailAction { TURN_LEFT,TURN_RIGHT,SHOOT; public String getDescription() { switch(this.ordinal()) { case 0: return "向左转"; case 1: return "向右转"; case 2: return "射击"; default: return null; } } }
可以用下面的代码测试所定义的方法是否可用。
public class DetailActionDemo { public static void main(String[] args) { for(DetailAction action : DetailAction.values()) { System.out.printf("%s: %s%n",action,action.getDescription()); } } }
运行结果:
TURN_LEFT: 向左转
TURN_RIGHT: 向右转
SHOOT: 射击
4.枚举类型既然是类,那么也就可以有构造函数。只不过不得有公开(Public)的构造函数,这是为了避免直接对枚举类型实例化。如下代码:
public class DetailActioin2 { TURN_LEFT("向左转"),TURN_RIGHT("向右转"),SHOOT("射击"); private String description; //不公开的构造函数 private DetailAction2(String description) { this.description = description; } public String getDescription() { return description; } }
非公开的构造函数最常见的一个例子就是singleton模式的应用,当某个类只能有一个实例时,可由类维护唯一的实例,这时可以将构造函数设定为私有,取用此类的开发人员就不能自行新增多个实例了。Singleton模式的简易版本代码如下:
public class Singleton { //构造函数私有,只限内部调用 private SingleTon(){}; private static Singleton instance = null; public static synchronized SingleTon getInstance() { if(instance == null) instance = new Singleton(); return instance; } }
4. 因值而异的类实现(Value-Specific Class Bodies)
这个功能简单地说像是在使用匿名内部类来实现Command模式,它可以为每个枚举值定义各自的类本体与方法实现。
一种实现方式如下:
public interface IDescription { public String getDescription(); } public enum MoreAction implements IDescription { TURN_LEFT { //实现接口上的方法 public String getString() {return "向左转"} }, //注意这里的枚举值分隔使用, TURN_RIGHT { //实现接口上的方法 public String getString() {return "向右转"} }, //注意这里的枚举值分隔使用, SHOOT { //实现接口上的方法 public String getString() {return "射击"} }; //注意这里的枚举值结束使用; }
每个枚举成员的{与}之间是类本体,还可以在其中如同定义类一样地声明数据成员或者数据方法。测试这段代码的程序如下:
public class MoreActionDemo { public static void main(String[] args) { for(MoreAction action : MoreAction.values()) { System.out.printf("%s: %s%n",action,action.getDescription()); } } }
这个例子是将因值而异的类实现用在返回枚举值的描述上,可以按照相同的方式,为每个枚举值加上一些各自的方法实现,而调用的接口是统一的。执行结果如下:
D:\Java_Test>javac IDescription.java
D:\Java_Test>javac MoreAction.java
D:\Java_Test>javac MoreActionDemo.java
D:\Java_Test>java MoreActionDemo
TURN_LEFT: 向左转
TURN_RIGHT: 向右转
SHOOT: 射击
可能是利用枚举类型实现的接口中的方法,这里直接用
D:\Java_Test>javac IDescription.java 编译时会提示找不到getDescription()方法,所以,只好挨个来编译了。
也可以运用抽象方法去改写上面的MoreAction.java,如下:
public enum MoreAction2 { TURN_LEFT{ //实现抽象方法 public String getDescription() { return "向左转"; } }, //记得这里的枚举值分隔使用, TURN_RIGHT{ //实现抽象方法 public String getDescription() { return "向右转"; } }, SHOOT{ //实现抽象方法 public String getDescription() { return "射击"; } }; //记得这里的枚举值结束使用; //声明抽象方法 public abstract String getDescription(); }
然后用MoreActionDemo2.java来测试:
public class MoreActionDemo2 { public static void main(String[] args) { for(MoreAction2 action : MoreAction2.values()) { System.out.printf("%s: %s%n",action,action.getDescription()); } } }
执行结果与上面相同。