[Java] enum 使用分析
背景
在 Java 语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组 int
常量。定义如下:
public class Color {
public static final RED = 1;
public static final GREEN = 2;
public static final BLUE = 3;
}
这种模式通常称作 int
枚举模式。可是这种模式存在什么问题呢?通常我们写出来的代码都会从它的 ** 安全性 、易用性和可读性** 等方面来考虑。首先我们可以来考虑这种方式的类型安全性。比如我们设计一个函数,要求传入红、绿、蓝的某一个值。但是使用 int
类型,我们无法保证传入的值为合法。
public String getColor(int color) {
String result = "";
switch (color) {
case Color.RED:
result = "红色";
break;
case Color.GREEN:
result = "绿色";
break;
case Color.BLUE:
result = "蓝色";
break;
default:
result = "未定义的颜色";
break;
}
return result;
}
public void doSomething() {
System.out.println( this.getColor(Color.RED)); //这是正常的使用场景
System.out.println(this.getColor(5)); //这是不正常的使用场景,编译器不会报错,这就导致了类型不安全的问题
}
程序 getColor(Color.RED)
是我们预期的使用方法。可 getColor(5)
显然就不是了,而且编译可以通过,在运行时就会出现意想不到的情况。这显然就不符合 Java
程序的类型安全。
接下来我们来考虑一下这种模式的可读性。使用枚举的大多数场合,我都需要方便得到枚举类型的字符串表达式。如果将 int
枚举常量打印出来,我们所见到的就是一组数字,这是没什么太大的用处。我们可能会想到使 String
常量代替 int
常量。虽然它为这些常量提供了可打印的字符串,但是它会导致性能问题,因为它依赖于字符串的比较操作,所以这种模式也是我们不期望的。 所以有必要介绍一种新的方式:枚举类型。
定义
枚举类型是指由一组固定的常量组成合法的类型,由 enum
关键字来定义一个枚举类型。下面就是 Java
枚举类型的定义:
public enum Color {
RED, GREEN, BLUE
}
特点
- 使用关键字
enum
- 类型名称
- 一串值
- 可以定义在单独的文件中,也可以嵌在
Java
文件中 - 可以实现一个或多个接口,
- 可以定义新的变量
- 可以定义新的方法
- 可以定义根据具体值而相异的类
从上面的特点中可以看出 Java
中的枚举类型很像一个特殊的 class
, 实际上 enum
声明确实定义的就是一个类。这些类继承自类库 (java.lang.Enum<E>)。通过对上面的 Color
反编译后我们可以发现这一事实:
// 被编译器加上final声明,故该类是无法继承的
public final class Color extends Enum
{
public static Color[] values()
{
return (Color[])$VALUES.clone();
}
// 实现 Enum 中的抽象方法
public static Color valueOf(String s)
{
return (Color)Enum.valueOf(Color, s);
}
// 私有构造器,外部不能动态创建一个枚举对象
private Color(String s, int i)
{
super(s, i);
}
//所有的枚举值都是静态常量
public static final Color RED;
public static final Color GREEN;
public static final Color BLUE;
private static final Color $VALUES[];
// 对枚举类的所有枚举值对象进行第一次初始化
static
{
RED = new Color("RED", 0);
GREEN = new Color("GREEN", 1);
BLUE = new Color("BLUE", 2);
$VALUES = (new Color[] {
RED, GREEN, BLUE
});
}
}
使用方法
1、Color
枚举类就是 class
,而且是一个不可以被继承的 final
类。其枚举值(RED, Green, BLUE)
都是 Color
类型的类静态常量, 所以我们可以通过下面的方式来得到 Color
枚举类的一个实例:
Color c=Color.RED;
2、即然枚举类是 class
,当然在枚举类型中有构造器,方法和数据域。但是,枚举类的构造器有很大的不同:
- (1) 构造器只能在构造枚举值的时候被调用。
- (2) 构造器只能私有
private
,绝对不允许有public
构造器。 这样可以保证外部代码无法新构造枚举类的实例。这也是完全符合情理的,因为我们知道枚举值是public static final
的常量而已。 但枚举类的方法和数据域可以允许外部访问。
3、所有枚举类都继承了 Enum
的方法,下面我们详细介绍这些方法。
- (1)
ordinal()
方法: 返回枚举值在枚举类中的顺序。这个顺序根据枚举值声明的顺序而定。
Color.RED.ordinal(); //返回结果:0
Color.BLUE.ordinal(); //返回结果:1 ```
* (2) `compareTo()`方法: `Enum` 实现了 `java.lang.Comparable` 接口,因此可以比较对象的顺序。`Enum` 中的 `compareTo` 返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出 `ClassCastException()` 异常。
Color.RED.compareTo(Color.BLUE); //返回结果 -1
* (3) `values()` 方法: 静态方法,返回一个包含全部枚举值的数组。
Color[] colors=Color.values();
for(Color c:colors {
System.out.print(c+",");
} //返回结果:RED, GREEN, BLUE
* (4) `toString()` 方法: 返回枚举常量的名称。
Color c = Color.RED;
System.out.println(c); //返回结果: RED
* (5) `valueOf()` 方法: 这个方法和`toString` 方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。
Color.valueOf("BLUE"); //返回结果: Color.BLUE
* (6) `equals()` 方法: 比较两个枚举类对象的引用。
## 总结
那么什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数、一年四季等。或者是在我们编译前就知道其包含的所有值的集合。Java 1.5的枚举能满足绝大部分程序员的要求的,它的简明,易用的特点是很突出的。