枚举类型是javaSE5中新加入的一个元素,它的功能比C++中枚举类型的功能完备的多,下面是一个简单的例子:
public enum Dana {
A,B,C,D
}
在这里创建了一个名为Dana的枚举类型,它有四个具名值,由于枚举类型的实例是常量,因此按照命名惯例它们都用大写字母表示(有多个单词则用下划线连接)
创建了一个枚举类型,那么怎么使用它呢,如下:
public static void main(String[] args) {
Dana dana = Dana.A;
System.out.println(dana);
}
}
这样就可以打印出枚举类型的值了,在创建enum时,编译器会自动添加一些有用的特性,比如,它会创建toString()方法,以便 你可以很方便地显示某个enum实例的名字,上面打印出来的效果也是这个原理,编译器还会创建ordinal()方法,用来表示某个特定enum常量的声明顺序,已经static values()方法,用来在按照enum常量的声明顺序,产生由这些常量值构成的数组:
public class EnumOrder {
public static void main(String[] args) {
for (Dana dana : Dana.values()) {
System.out.println(dana + ",ordinal " + dana.ordinal());
}
}
}
打印效果:
尽管enum看起来像是一种新的数据类型,但是这个关键字只是为enum生成对应的类时产生了某些编译器行为,因此在很大程度上可以将enum当作其他任何类来处理,事实上,enum确实是类,并且有自己的方法。可以创建一个enum类,把它看做一个普通的类。除了它不能继承其他类了。(java是单继承,它已经继承了Enum),可以添加其他方法,覆盖它本身的方法
下面我们看看怎么向enum中添加新方法:
public enum Dana {
A("My name yi A"),
B("My name yi B"),
C("My name yi C"),
D("My name yi D");
private String description;
private Dana (String description){
this.description=description;
}
public String getDescription(){
return description;
}
public static void main(String[] args) {
for(Dana en : Dana.values()){
System.out.println(en+": "+en.getDescription());
}
}
}
打印效果为:
注意,如果你打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号,同时,java要求你必须先定义enum实例,如果在定义enum实例之前定义了任何方法或属性,那么在编译时就会得到错误信息。
在这个例子中,我们的构造器是私有的,但对于它的可访问性而言并没有什么变化,因为即使我们不声明为私有的,我们也只能在enum定义的内部使用其构造器创建enum实例,一旦enum的定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。
再看一个覆盖enum的方法的例子:
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
public static void main(String[] args) {
for(Color c : values()){
System.out.println(c);
}
}
}
效果如下:
另外,enum有一个特别实用的特性,就是它可以在switch语句中使用:
下面是一个例子:
enum Signal {GREEN,YELLOW,RED }
public class TrafficLight {
Signal color= Signal.RED;
public void change(){
switch(color){
case RED : color = Signal.GREEN;
break;
case GREEN : color = Signal.YELLOW;
break;
case YELLOW : color =Signal.RED;
break;
}
}
public String toString() {
return "The traffic light is " + color;
}
public static void main(String[] args) {
TrafficLight t =new TrafficLight();
for(int i=0;i<7;i++){
System.out.println(t);
t.change();
}
}
}
打印效果为:
由于switch是要在有限的可能值集合中进行选择,因此它与enum正是绝配,请注意enum的名字是如何能够倍加清楚地表明程序意欲何为的。
下面我们说一下把静态导入用于enum,先看一个例子:
package com.enumDemo;
import static com.enumDemo.Enum.*;
public class Test {
Enum degree;
public Test(Enum degree){
this.degree=degree;
}
public String toString(){
return "Test is "+degree;
}
public static void main(String[] args) {
System.out.println(new Test(A));
System.out.println(new Test(B));
System.out.println(new Test(C));
}
}
运行效果为:
使用statica import能够将enum实例的标识符带入当前的命名空间,所以无需再使用enum类型来修饰enum实例。这是一个好办法吗?还是显示地修饰enum实例更好呢?这要看代码得复杂程度了,编译器可以确保你使用的是正确的类型,唯一需要担心的是,使用静态导入会不会使你的代码看起来令人难以理解。在多数情况下,使用静态导入还是有好处的,不过这要具体情况具体分析了。
这里要特别注意,在定义enum的同一个文件中,这种技巧无法使用,如果是在默认包中定义enum,这种技巧也无法使用。(在Sun的内部对这一点也是持有不同意见,这里先不去讨论)
其实在创建enum时,编译器会为你生成一个相关的类,这个类时继承自java.lang.Enum,这里面有个有意思的问题,enum里面有个values()方法,但这个方法却不属于Enum,这是怎么回事呢,原理,values()是由编译器添加的static方法,可以看出,在创建enum的时候编译器还为其添加了valueOf()方法。由于values()方法是由编译器插入到enum定义中的static方法,所以,如果你将enum实例向上转型为Enum,那么values()方法就不可访问了,不过在Class中有一个getEnumConstants()方法,所以即便Enum接口中没有values()方法,我们仍然可以通过Class对象取得所有enum实例:如:
enum Search {
HITHER, YOU
}
public class UpcastEnum {
public static void main(String[] args) {
// Search[] vals = Search.values();
Enum e = Search.HITHER;
for (Enum en : e.getClass().getEnumConstants())
System.out.println(en);
}
}
效果如下:
getEnumConstants()是Class上的方法,所以你甚至可以对不是每句的类调用此方法:
只不过,此时方法会返回null,所以当你试图使用其返回的结果时会发生异常。如下:
public class NonEnum {
public static void main(String[] args) {
Class<Integer> intClass = Integer.class;
try {
for (Object en : intClass.getEnumConstants())
System.out.println(en);
} catch (Exception e) {
System.out.println(e);
}
}
}
运行效果如下: