枚举的本质

1. 枚举的基本使用

枚举使用 enum 这个关键字来定义,例如为了表示衣服的尺寸定义一个枚举类型 Size

1
2
3
public enum Size {
SMALL, MEDIUM, LARGE
}

Size 包括三个值,分别表示小、中、大,值一般是大写字母,多个值之间以逗号分隔

可以这样使用 Size

1
Size size = Size.MEDIUM;

Size size 声明了一个变量 size,它的类型是 Sizesize=Size.MEDIUM 将枚举值 MEDIUM 赋值给 size 变量

枚举变量的 toString() 方法返回其字面值,枚举类型还有一个 name() 方法也返回其字面值

1
2
3
Size size = Size.MEDIUM;
System.out.println(size.toString());
System.out.println(size.name());

输出

1
2
MEDIUM
MEDIUM

枚举值可以使用 equals()​​ 方法和 ==​​ 进行比较,结果相同

1
2
3
4
Size size = Size.MEDIUM;
System.out.println(size == Size.MEDIUM);
System.out.println(size == Size.LARGE);
System.out.println(size.equals(Size.MEDIUM));

输出为

1
2
3
true
false
true

枚举类型都有一个方法 int ordinal(),返回枚举值在声明时的顺序,从 0 开始

1
2
3
4
5
6
Size s0 = Size.SMALL;
Size s1 = Size.MEDIUM;
Size s2 = Size.LARGE;
System.out.println(s0.ordinal());
System.out.println(s1.ordinal());
System.out.println(s2.ordinal());

输出为

1
2
3
0
1
2

枚举类型都实现了 Comparable 接口,可以通过方法 compareTo​​ 与其他枚举值进行比较,其实就是比较 ordinal()​​ 方法返回值的大小

1
2
Size s0 = Size.SMALL;
System.out.println(s0.compareTo(Size.MEDIUM));

输出 -1 表明 SMALL ​小于 MEDIUM

枚举变量可以用于方法参数、类变量、实例变量等,最常用的还是用于 switch 语句

1
2
3
4
5
6
7
8
9
10
11
12
Size s0 = Size.SMALL;
switch (s0){
case SMALL:
System.out.println("chose small");
break;
case MEDIUM:
System.out.println("chose medium");
break;
case LARGE:
System.out.println("chose large");
break;
}

注意在 switch ​语句内部,枚举值不能带枚举类型前缀,例如,直接使用 SMALL,不能使用 Size.SMALL

枚举类型还有一个静态方法 values() 返回一个包含所有枚举值的数组,顺序和声明时的顺序一致

1
2
3
for (Size size : Size.values()) {
System.out.println(size);
}

输出

1
2
3
SMALL
MEDIUM
LARGE

2. 枚举的好处

  • 定义枚举的语法更为简洁
  • 枚举更为安全。一个枚举类型的变量,它的值要么为 null,要么为枚举值之一,不可能为其他值,但使用整型变量,它的值就没有办法强制,值可能就是无效的。
  • 枚举类型自带很多便利方法(如 valuesvalueOftoString ​等),易于使用。

3. 枚举的底层原理

枚举类型实际上会被 Java 编译器转换成一个对应的类,这个类型继承了 java.lang.Enum​​ 类。

Enum 类有 nameordinal 两个实例变量,在构造方法中需要传递。

name()toString()ordinal()compareTo()equals() 方法都是由 Enum 类根据其实例变量 nameordinal 实现的。

values()valueOf() 方法是编译器给每个枚举类型自动添加的。

编译器转换后的代码大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class Size extends Enum {
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 final 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);
}
}

Sizefinal 的,所以枚举类型不能被继承,枚举值也都是 final 的,所以定义出来了就不能被修改

4. 更多使用场景

实际使用时枚举中可能会有关联的实例变量和方法,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}
}

上述代码定义了两个实例变量 abbrtitle,以及对应的 get 方法,分别表示缩写和中文名称;定义了一个私有构造方法,接受缩写和中文名称,每个枚举值在定义的时候都传递了对应的值。

注意: 枚举值的定义需要放在最上面,枚举值写完之后,要以分号 ; 结尾,然后才能写其他代码