Java基础系列(二十):枚举入门

关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件来使用,这种新的类型就是枚举类。

比如这就是一个最简单的枚举类

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

我们在实际中如何应用枚举类呢,请看下面的程序代码:

1
2
3
4
5
6
7
8
9
public class EnumTest {
 
    public static void main(String[] args) {
        System.out.println(Size.EXTRA_LARGE);
        System.out.println(Size.SMALL);
        System.out.println(Size.MEDIUM);
        System.out.println(Size.LARGE);
    }
}

打印结果:

1
2
3
4
EXTRA_LARGE
SMALL
MEDIUM
LARGE

我们也可以给这些枚举值赋予一个描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Size {
     SMALL("小"),MEDIUM("中"),LARGE("大"),EXTRA_LARGE("特大");
 
    private String description;
 
    public String getDescription() {
        return description;
    }
 
    private Size(String description) {
        this.description = description;
    }
}

运行这个程序:

1
2
3
4
5
6
7
8
9
public class EnumTest {
 
    public static void main(String[] args) {
        System.out.println(Size.EXTRA_LARGE + " : " + Size.EXTRA_LARGE.getDescription());
        System.out.println(Size.SMALL + " : " + Size.SMALL.getDescription());
        System.out.println(Size.MEDIUM + " : " +Size.MEDIUM.getDescription());
        System.out.println(Size.LARGE + " : " + Size.LARGE.getDescription());
    }
}

打印结果:

1
2
3
4
EXTRA_LARGE : 特大
SMALL : 小
MEDIUM : 中
LARGE : 大

这段程序需要注意一点,枚举类中是允许我们自由的添加构造器或者我们自己定义的方法的,但是必须在enum实例序列的最后添加一个分号。同时,Java要求你必须先定义enum实例。如果在定义enum实例之前定义了任何方法或属性,那么在编译时就会得到错误信息。

Enum API介绍

所有的枚举类型都是Enum类的子类,它们继承了这个类的许多有用的方法,其中比较有用的有以下五个比较有用的方法,我们下面来一一道来:

toString

说起来方法,最离不开的就是toString,我们通过上面的例子其实就可以看出,toString方法重写后返回的其实就是枚举实例的名字:

1
2
3
4
//这是Emum类的源码,枚举的toString实现,和我们想象的完全一致
public String toString() {
    return name;
}

同样返回name的还有一个方法:

1
2
3
public final String name() {
    return name;
}

valueOf

直接看源码:

1
2
3
4
5
6
7
8
9
10
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
}

可以看出这个静态方法就是给某个枚举类实例设置为一个该枚举类中的一个实例值,一般用法如下:

1
2
3
4
5
6
7
8
public class EnumTest {
 
    public static void main(String[] args) {
 
        Size s = Enum.valueOf(Size.class,"EXTRA_LARGE");
        System.out.println(s.getDescription());
    }
}

打印结果:

1
特大

这里需要注意,第二个参数不能写成枚举类中不存在的实例,否则就会抛出一个异常。

ordinal

这个方法返回到是枚举常量在enum声明中的位置,位置从0开始计数,具体用法如下:

1
2
3
4
5
6
7
8
9
10
11
public class EnumTest {
 
    public static void main(String[] args) {
 
        System.out.println(Size.SMALL.ordinal() + " : " + Size.SMALL.getDescription());
        System.out.println(Size.MEDIUM.ordinal() + " : " +Size.MEDIUM.getDescription());
        System.out.println(Size.LARGE.ordinal() + " : " + Size.LARGE.getDescription());
        System.out.println(Size.EXTRA_LARGE.ordinal() + " : " + Size.EXTRA_LARGE.getDescription());
 
    }
}

打印结果:

1
2
3
4
0 : 小
1 : 中
2 : 大
3 : 特大

compareTo

1
2
3
4
5
6
7
8
9
//源码
public final int compareTo(E o) {
    Enum<?> other = (Enum<?>)o;
    Enum<E> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
}

可以看出compareTo返回的是如果调用这个方法的枚举常量在other之前,返回一个负值;如果this == other,则返回0;否则返回正值。枚举常量的出现次序在enum声明中指出。

values

values方法返回的是一个包含全部枚举值的数组,但是我们翻遍了源码,并没有发现这个方法,于是后来查阅了资料,通过反射机制才发现这个方法是由编译器添加的static方法。使用方法如下

1
2
3
4
5
6
7
8
9
10
11
public class EnumTest {
 
    public static void main(String[] args) {
 
        Size[] sizes = Size.values();
        for (Size size : sizes) {
            System.out.println(size.name() + ": " + size.getDescription());
        }
 
    }
}

打印结果:

1
2
3
4
SMALL: 小
MEDIUM: 中
LARGE: 大
EXTRA_LARGE: 特大