概念
所谓枚举,是一种特殊的数据,它的取值是有限的,可以枚举出来的, 比如说一年就是有四季、一周有七天,虽然使用类也可以处理这种数据,但枚举类型更为简洁、安全和方便。
基本用法
- 定义
public enum Size {
SMALL, MEDIUM, LARGE
}
枚举类型可以定义为一个单独的文件,也可以定义在其他类内部。
- 使用
Size size = Size.SMALL;
System.out.println(size.toString());
System.out.println(size.name());
输出都是SMALL
Size size = Size.SMALL;
System.out.println(size==Size.SMALL);
System.out.println(size.equals(Size.SMALL));
System.out.println(size==Size.MEDIUM);
分别是true, true, false
Size size = Size.MEDIUM;
System.out.println(size.ordinal());
枚举类型都有一个方法int ordinal(),表示枚举值在声明时的顺序,从0开始
Size size = Size.SMALL;
System.out.println(size.compareTo(Size.MEDIUM));
比较其实就是比较ordinal的大小
static void onChosen(Size size){
switch(size){
case SMALL:
System.out.println("chosen small"); break;
case MEDIUM:
System.out.println("chosen medium"); break;
case LARGE:
System.out.println("chosen large"); break;
}
}
在switch语句内部,枚举值不能带枚举类型前缀,例如,直接使用SMALL,不能使用Size.SMALL
for(Size size : Size.values()){
System.out.println(size);
}
输出为三行,分别是SMALL, MEDIUM, LARGE
枚举的好处
Java是从JDK 5才开始支持枚举的,在此之前,一般是在类中定义静态整形变量来实现类似功能
定义枚举的语法更为简洁。
枚举更为安全,一个枚举类型的变量,它的值要么为null,要么为枚举值之一,不可能为其他值,但使用整形变量,它的值就没有办法强制,值可能就是无效的。
枚举类型自带很多便利方法(如values, valueOf, toString等),易于使用
基本实现原理
枚举类型实际上会被Java编译器转换为一个对应的类,这个类继承了Java API中的java.lang.Enum类
public final class Size extends Enum<Size> {
public static final Size SMALL = new Size("SMALL",0);
public static final Size MEDIUM = new Size("MEDIUM",1);
public static final Size LARGE = new Size("LARGE",2);
private static Size[] VALUES =
new Size[]{SMALL,MEDIUM,LARGE};
private Size(String name, int ordinal){
super(name, ordinal);
}
public static Size[] values(){
Size[] values = new Size[VALUES.length];
System.arraycopy(VALUES, 0,
values, 0, VALUES.length);
return values;
}
public static Size valueOf(String name){
return Enum.valueOf(Size.class, name);
}
}
Size是final的,不能被继承,Enum<Size>表示父类,<Size>是泛型写法,我们后续文章介绍,此处可以忽略。
Size有一个私有的构造方法,接受name和ordinal,传递给父类,私有表示不能在外部创建新的实例。
三个枚举值实际上是三个静态变量,也是final的,不能被修改。
values方法是编译器添加的,内部有一个values数组保持所有枚举值。
valueOf方法调用的是父类的方法,额外传递了参数Size.class,表示类的类型信息,类型信息我们后续文章介绍, 父类实际上是回过头来调用values方法,根据name对比得到对应的枚举值的。
一般枚举变量会被转换为对应的类变量,在switch语句中,枚举值会被转换为其对应的ordinal值。
- 注意点
当需要id标记时,最好别用ordinal值
高级用法
枚举类型可以声明抽象方法,每个枚举值中可以实现该方法,也可以重写枚举类型的其他方法。
public enum Size {
SMALL {
@Override
public void onChosen() {
System.out.println("chosen small");
}
},MEDIUM {
@Override
public void onChosen() {
System.out.println("chosen medium");
}
},LARGE {
@Override
public void onChosen() {
System.out.println("chosen large");
}
};
public abstract void onChosen();
}
public enum Size {
SMALL {
@Override
public void onChosen() {
System.out.println("chosen small");
}
},MEDIUM {
@Override
public void onChosen() {
System.out.println("chosen medium");
}
},LARGE {
@Override
public void onChosen() {
System.out.println("chosen large");
}
};
public abstract void onChosen();
}
switch的缺陷是,定义swich的代码和定义枚举类型的代码可能不在一起, 如果新增了枚举值,应该需要同样修改switch代码,但可能会忘记,而如果使用抽象方法,则不可能忘记,在定义枚举值的同时,编译器会强迫同时定义相关行为代码。所以,如果行为代码和枚举值是密切相关的,使用以上写法可以更为简洁、安全、容易维护。