如何使用Java中的Enum类 定义一个 enum enum 类 enum 的用法 EnumSet 和 EnumMap 参考文献

Java1.5 中出现了枚举类型。当一个值都在一个固定的范围内变化,那就可以使用 enum 类型来定义。比如说,一周有七天,一年有四季。

没有枚举类的时候,我们用常量来定义一组范围值的:

public static class Season {
		public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

通过常量定义的方式有这样几个缺点:

  1. 类型不安全。如 Season 类所示,程序执行过程中接收的是任意一个 int 类型的值,完全可能传入一个 1 到 4 之外的值,导致错误的出现。

  2. 一致性差。int 枚举属于编译期常量,编译完成后,代码中引用的地方会直接将整数值写入。也就是说,该 Int 枚举被修改之后,所有引用它的程序都需要重新编译。

  3. 类型无指意性。Seaon 枚举值仅仅是一些无任何含义的整数值,调试期间仅仅是一些魔数。

《阿里巴巴Java开发手册(华山版)》中建议我们这样定义一个 enum 类:

【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。

正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。

由此,我们可以定义一个季节的枚举类,如下所示:

public enum SeasonEnum {
    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);

    private int seq;

    SeasonEnum(int seq) {
        this.seq = seq;
    }

    public int getSeq() {
        return seq;
    }
}

enum 类

使用 enum 定义的枚举类默认继承 java.lang.Enum

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
	...
}

如何使用Java中的Enum类
定义一个 enum
enum 类
enum 的用法
EnumSet 和 EnumMap
参考文献

变量

// enum 实例的名字
private final String name;
// 定义enum实例的顺序,从0开始计数
private final int ordinal;

方法

// 返回enum实例的名称,eg. SeasonEnum.SPRING.name() - output: SPRING
public final String name() { return name;}
// 返回enum实例的顺序,eg. SeasonEnum.SPRING.ordinal() - output: 0
public final int ordinal() { return ordinal;}
// enum实例 -> 字符串 eg. SeasonEnum.SPRING.toString() - output: SPRING
public String toString() { return name;}
// 比较是不是一个constant,即两个enum实例的顺序(oridnal)是否相同
public final int compareTo(E o) { ... return self.ordinal - other.ordinal; }
// 比较是不是同一个对象
public final boolean equals(Object other) { return this==other;}
// 返回声明枚举常量的类的类对象
public final Class<E> getDeclaringClass(){...}
// 返回指定name的enum实例
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {}

// values()是编译器产生的一个方法,API中没有,可以用来遍历
public static SeasonEnum getSeasonByOrdinal(int seq) {
	for (SeasonEnum season: SeasonEnum.values()) {
		if(season.getSeq() == seq) {
			return season;
		}
	}
	return null;
}

enum 的用法

用法一:定义常量

也就是最开始说的,enum 出现之后,用 enum 来代替常量类型。

public static class Season {
		public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

public enum SeasonEnum {
    SPRING, SUMMER, AUTUMN, WINTER;
}

用法二:switch

Java1.5 推出了 enum,Java1.6 支持在 switch 中使用 enum 类型。


public enum OperationUseSwitch {
	PLUS, MINUS, TIMES, DIVIDE;
 
	double apply(double x, double y) {
		switch (this) {
			case PLUS:
				return x + y;
			case MINUS:
				return x - y;
			case TIMES:
				return x * y;
			case DIVIDE:
				return x / y;
		}
		// 如果this不属于上面四种操作符,抛出异常
		throw new AssertionError("Unknown operation: " + this);
	}
}

用法三:特定于常量的方法实现

特定于常量的方法实现指的是,在 enum 类中定义一个抽象方法,然后各个枚举常量需要实现这个方法。这样做的优点在于,相对于 switch 语句,抽象程度更高,每个 enum 实例都需要实现统一的方法,不会漏下。

public enum Operation {
	PLUS {
		double apply(double x, double y) {
			return x + y;
		}
	},
	MINUS {
		double apply(double x, double y) {
			return x - y;
		}
	},
	TIMES {
		double apply(double x, double y) {
			return x * y;
		}
	},
	DIVIDE {
		double apply(double x, double y) {
			return x / y;
		}
	};
 
	abstract double apply(double x, double y);
}

用法四:实现接口

enum 类都隐式得继承自 java.lang.Enum,而 Java 只支持单继承,所有 enum 类不能再继承其它类,但是可以实现接口。比如用法三中将统一的方法抽象出来,就可以使用接口来实现:

public interface Behavior {
    double apply(double x, double y);
}

public enum  Operation implements Behavior{
    PLUS {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        public double apply(double x, double y) {
            return x / y;
        }
    };
}

用法五:覆盖 Enum 的方法

public class Test {
    public enum Color {
        RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
        // 成员变量
        private String name;
        private int index;

        // 构造方法
        private Color(String name, int index) {
            this.name = name;
            this.index = index;
        }

        // 覆盖方法
        @Override
        public String toString() {
            return this.index + "_" + this.name;
        }
    }

    public static void main(String[] args) {
        System.out.println(Color.RED.toString());
    }
}

EnumSet 和 EnumMap

EnumSet 和 EnumMap 是两个为枚举而设计的集合。EnumSet保证集合中的元素不重复;EnumMap中的 key 是enum 类型,而 value 则可以是任意类型。

EnumMap

public class Herb {
    public enum Type { ANNUAL, PERENNIAL, BIENNIAL }
 
    private final String name;
    private final Type type;
    
    Herb(String name, Type type) {
        this.name = name;
        this.type = type;
    }
 
    @Override public String toString() {
        return name;
    }
}

public static void main(String[] args) {
    Herb[] garden = { new Herb("Basil", Type.ANNUAL),
            new Herb("Carroway", Type.BIENNIAL),
            new Herb("Dill", Type.ANNUAL),
            new Herb("Lavendar", Type.PERENNIAL),
            new Herb("Parsley", Type.BIENNIAL),
            new Herb("Rosemary", Type.PERENNIAL) };
 
    // Using an EnumMap to associate data with an enum - Page 162
    Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<Herb.Type, Set<Herb>>(
            Herb.Type.class);
    for (Herb.Type t : Herb.Type.values())
        herbsByType.put(t, new HashSet<Herb>());
    for (Herb h : garden)
        herbsByType.get(h.type).add(h);
    System.out.println(herbsByType);
 
}

EnumSet

EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。EnumSet 提供了许多工厂方法以便于初始化。

方法名称 描述
allOf(Class element type) 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象
complementOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象, 并包含所有 s 中未包含的枚举成员
copyOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象, 并与 s 包含相同的枚举成员
noneOf(<Class elementType) 创建指定枚举类型的空 EnumSet 对象
of(E first,e...rest) 创建包含指定枚举成员的 EnumSet 对象
range(E from ,E to) 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚 举成员

EnumSet 作为 Set 接口实现,它支持对包含的枚举常量的遍历。

for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
    doSomeThing(op);
}

参考文献

W3CSchool-Java枚举

Java 语言中 Enum 类型的使用介绍

Java 枚举(enum) 详解7种常见的用法

Java枚举 —— 很少被使用,或许是因为真正了解它的人太少了

Java枚举(enum)详解:Java声明枚举类型、枚举(enum)类、EnumMap 与 EnumSet