1. 基础
1.1 基本用法
定义:enum
public enum Size {
SMALL, MEDIUM, LARGE
}
toString()
方法返回其字面值,所有枚举类型也都有一个name()方法,返回值与toString()一样。
Size size = Size.SMALL;
System.out.println(size.toString());
System.out.println(size.name());
----
Output: "SMALL"
valueOf(String)
返回字符串对应的枚举值。
System.out.println(Size.SMALL==Size.valueOf("SMALL"));
---
Output: true
values()
返回一个包括所有枚举值的数组,顺序与声明时的顺序一致。
for(Size size : Size.values()){
System.out.println(size);
}
---
Output: SMALL, MEDIUM, LARGE
int ordinal()
表示枚举值在声明时的顺序,从0开始。
Size size = Size.MEDIUM;
System.out.println(size.ordinal());
---
Output: 1
1.2 枚举的好处
枚举更为安全,一个枚举类型的变量,它的值要么为null,要么为枚举值之一,不可能为其他值,但使用整形变量,它的值就没有办法强制,值可能就是无效的。
枚举类型自带很多便利方法(如values, valueOf, toString等),易于使用。
1.3 实现原理
枚举类型实际上会被Java编译器转换为一个对应的类,这个类继承了Java API中的java.lang.Enum
类。
Enum类有两个实例变量name
和ordinal
,在构造方法中需要传递,name(), toString(), ordinal(), compareTo(), equals()
方法都是由Enum类根据其实例变量name和ordinal实现的。
values和valueOf方法是编译器给每个枚举类型自动添加的,上面的枚举类型Size转换后的普通类的代码大概如下所示:
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有一个私有的构造方法,接受name
和ordinal
,传递给父类,私有表示不能在外部创建新的实例。
三个枚举值实际上是三个静态变量,也是final的,不能被修改。
values方法是编译器添加的,内部有一个values数组
保持所有枚举值。
valueOf方法调用的是父类的方法,额外传递了参数Size.class,表示类的类型信息,父类实际上是回过头来调用values方法,根据name对比得到对应的枚举值的。
2. 典型场景
public enum Size {
SMALL("S","小号"),
MEDIUM("M","中号"),
LARGE("L","大号");
private String abbr;
private String title;
private Size(String abbr, String title){
this.abbr = abbr;
this.title = title;
}
public String getAbbr() {
return abbr;
}
public String getTitle() {
return title;
}
public static Size fromAbbr(String abbr){
for(Size size : Size.values()){
if(size.getAbbr().equals(abbr)){
return size;
}
}
return null;
}
}
2.2 实现原理
public final class Size extends Enum<Size> {
public static final Size SMALL =
new Size("SMALL",0, "S", "小号");
public static final Size MEDIUM =
new Size("MEDIUM",1,"M","中号");
public static final Size LARGE =
new Size("LARGE",2,"L","大号");
private String abbr;
private String title;
private Size(String name, int ordinal,
String abbr, String title){
super(name, ordinal);
this.abbr = abbr;
this.title = title;
}
//... 其他代码
}
每个枚举值经常有一个关联的标示(id),通常用int整数表示,使用整数可以节约存储空间,减少网络传输。一个自然的想法是使用枚举中自带的ordinal值,但ordinal并不是一个好的选择。
为什么呢?因为ordinal的值会随着枚举值在定义中的位置变化而变化,但一般来说,我们希望id值和枚举值的关系保持不变,尤其是表示枚举值的id已经保存在了很多地方的时候。
3. 高级用法
枚举还有一些高级用法,比如说,每个枚举值可以有关联的类定义体,枚举类型可以声明抽象方法,每个枚举值中可以实现该方法,也可以重写枚举类型的其他方法。
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();
}
这种写法内部是怎么实现的呢?每个枚举值都会生成一个类,这个类继承了枚举类型对应的类,然后再加上值特定的类定义体代码,枚举值会变成这个子类的对象,具体代码我们就不赘述了。