Files
OnJava8/docs/book/22-Enumerations.md
2019-08-09 22:12:43 +08:00

7.5 KiB
Raw Blame History

[TOC]

第二十二章 枚举

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

初始化和清理这章结束的时候我们已经简单地介绍了枚举的概念。现在你对Java已经有了更深刻的理解因此可以更深入地学习 Java 中的枚举了。你将在本章中看到使用enum可以做很多有趣的事情同时我们也会深入其他的Java特性例如泛型和反射。在这个过程中我们还将学习一些设计模式。

基本 enum 特性

我们已经在初始化和清理这章章看到调用enum的values()方法可以遍历enum实例 .values()方法返回enum实例的数组而且该数组中的元素严格保持其在enum中声明时的顺序因此你可以在循环中使用values()返回的数组。

创建enum时编译器会为你生成一个相关的类这个类继承自Java.lang.Enum。下面的例子演示了Enum提供的一些功能

// enums/EnumClass.java
// Capabilities of the Enum class
enum Shrubbery { GROUND, CRAWLING, HANGING }
public class EnumClass {
    public static void main(String[] args) {
        for(Shrubbery s : Shrubbery.values()) {
            System.out.println(
                    s + " ordinal: " + s.ordinal());
            System.out.print(
                    s.compareTo(Shrubbery.CRAWLING) + " ");
            System.out.print(
                    s.equals(Shrubbery.CRAWLING) + " ");
            System.out.println(s == Shrubbery.CRAWLING);
            System.out.println(s.getDeclaringClass());
            System.out.println(s.name());
            System.out.println("********************");
        }
// Produce an enum value from a String name:
        for(String s :
                "HANGING CRAWLING GROUND".split(" ")) {
            Shrubbery shrub =
                    Enum.valueOf(Shrubbery.class, s);
            System.out.println(shrub);
        }
    }
}

输出:

GROUND ordinal: 0
-1 false false
class Shrubbery
GROUND
********************
CRAWLING ordinal: 1
0 true true
class Shrubbery
CRAWLING
********************
HANGING ordinal: 2
1 false false
class Shrubbery
HANGING
********************
HANGING
CRAWLING
GROUND

ordinal()方法返回一个int值这是每个enum实例在声明时的次序从0开始。可以使用==来比较enum实例编译器会自动为你提供equals()和hashCode()方法。Enum类实现了Comparable接口所以它具有compareTo()方法。同时它还实现了Serializable接口。

如果在enum实例上调用getDeclaringClass()方法我们就能知道其所属的enum类。

name()方法返回enum实例声明时的名字这与使用toString()方法效果相同。valueOf()是在Enum中定义的static方法它根据给定的名字返回相应的enum实例如果不存在给定名字的实例将会抛出异常。

将静态类型导入用于 enum

先看一看 初始化和清理 这章中Burrito.java的另一个版本

// enums/SpicinessEnum.java
package enums;
public enum SpicinessEnum {
    NOT, MILD, MEDIUM, HOT, FLAMING
}
// enums/Burrito2.java
// {java enums.Burrito2}
package enums;
import static enums.SpicinessEnum.*;
public class Burrito2 {
    SpicinessEnum degree;
    public Burrito2(SpicinessEnum degree) {
        this.degree = degree;
    }
    @Override
    public String toString() {
        return "Burrito is "+ degree;
    }
    public static void main(String[] args) {
        System.out.println(new Burrito2(NOT));
        System.out.println(new Burrito2(MEDIUM));
        System.out.println(new Burrito2(HOT));
    }
}

输出为:

Burrito is NOT
Burrito is MEDIUM
Burrito is HOT

使用static import能够将enum实例的标识符带入当前的命名空间所以无需再用enum类型来修饰enum实例。这是一个好的想法吗或者还是显式地修饰enum实例更好这要看代码的复杂程度了。编译器可以确保你使用的是正确的类型所以唯一需要担心的是使用静态导入会不会导致你的代码令人难以理解。多数情况下使用static import还是有好处的不过程序员还是应该对具体情况进行具体分析。

注意在定义enum的同一个文件中这种技巧无法使用如果是在默认包中定义enum这种技巧也无法使用在Sun内部对这一点显然也有不同意见

方法添加

除了不能继承自一个enum之外我们基本上可以将enum看作一个常规的类。也就是说我们可以向enum中添加方法。enum甚至可以有main()方法。

一般来说我们希望每个枚举实例能够返回对自身的描述而不仅仅只是默认的toString()实现,这只能返回枚举实例的名字。为此,你可以提供一个构造器,专门负责处理这个额外的信息,然后添加一个方法,返回这个描述信息。看一看下面的示例:

// enums/OzWitch.java
// The witches in the land of Oz
public enum OzWitch {
    // Instances must be defined first, before methods:
    WEST("Miss Gulch, aka the Wicked Witch of the West"),
    NORTH("Glinda, the Good Witch of the North"),
    EAST("Wicked Witch of the East, wearer of the Ruby " +
            "Slippers, crushed by Dorothy's house"),
    SOUTH("Good by inference, but missing");
    private String description;
    // Constructor must be package or private access:
    private OzWitch(String description) {
        this.description = description;
    }
    public String getDescription() { return description; }
    public static void main(String[] args) {
        for(OzWitch witch : OzWitch.values())
            System.out.println(
                    witch + ": " + witch.getDescription());
    }
}

输出为:

WEST: Miss Gulch, aka the Wicked Witch of the West
NORTH: Glinda, the Good Witch of the North
EAST: Wicked Witch of the East, wearer of the Ruby
Slippers, crushed by Dorothy's house
SOUTH: Good by inference, but missing

注意如果你打算定义自己的方法那么必须在enum实例序列的最后添加一个分号。同时Java要求你必须先定义enum实例。如果在定义enum实例之前定义了任何方法或属性那么在编译时就会得到错误信息。

enum中的构造器与方法和普通的类没有区别因为除了有少许限制之外enum就是一个普通的类。所以我们可以使用enum做许多事情虽然我们一般只使用普通的枚举类型

在这个例子中虽然我们有意识地将enum的构造器声明为private但对于它的可访问性而言其实并没有什么变化因为即使不声明为private我们只能在enum定义的内部使用其构造器创建enum实例。一旦enum的定义结束编译器就不允许我们再使用其构造器来创建任何实例了。

switch语句

values方法

实现而非继承

随机选择

使用接口组织

使用EnumSet替代Flags

使用EnumMap

常量特定方法

多次调度

本章小结