文章目录

  • 深入理解枚举类
  • 简介
  • 枚举的好处
  • 枚举的典型应用
  • 使用规范
  • 枚举的本质
  • 枚举类的声明
  • 枚举的方法
  • 枚举的特性
  • 基本特性
  • 枚举可以添加方法
  • 枚举可以添加普通方法、静态方法、抽象方法、构造方法
  • 枚举可以实现接口
  • 枚举的应用
  • 组织常量
  • 状态机
  • 错误码
  • 组织枚举
  • 枚举工具类
  • EnumSet
  • EnumMap
  • 参考资料


深入理解枚举类

简介

enum 全称是 enumeration,是 JDK1.5 引入的新特性,位于Java.lang 包下。
在Java中 enum 是一个关键字,被 enum 修饰的类型就是枚举类。

public enum ColorEnum{
    BLACK,WHITE,GREEN;
}

枚举的好处

将枚举类型用作set或map的类型时,专用且高效。(引自枚举类的介绍)

枚举的典型应用

如 状态码、常量、颜色、类别等

使用规范

  • 枚举类名带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
    正例:枚举名字为ProcessStatusEnum的成员名称:SUCCESS / UNKNOWN_REASON
  • 所有的枚举类型字段必须要有注释,说明每个数据项的用途

引自阿里巴巴开发手册

枚举的本质

枚举类的声明

package java.lang;

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

下面我们新建一个枚举类`ColorEnum``

public enum ColorEnum{
    BLACK,WHITE,GREEN;
}
  1. 执行javac ColorEnum.java 命令,生成ColorEnum.class 文件.
  2. 接着执行javap ColorEnum.class 命令,输入如下内容:
public final class com.sun.advanced.learnenum.ColorEnum extends java.lang.Enum<com.sun.advanced.learnenum.ColorEnum> {
  public static final com.sun.advanced.learnenum.ColorEnum BLACK;
  public static final com.sun.advanced.learnenum.ColorEnum WHITE;
  public static final com.sun.advanced.learnenum.ColorEnum GREEN;
  public static com.sun.advanced.learnenum.ColorEnum[] values();
  public static com.sun.advanced.learnenum.ColorEnum valueOf(java.lang.String);
  static {};
}

从上面可以看出,枚举的本质是java.lang.Enum的子类。
尽管enum看起来很像一种新的数据类型,但实际上,enum是一种受限制的类,并且有自己的方法
枚举类被修饰为final,所以不能被其他类所继承。
定义的枚举值被修饰为static final,本质上枚举值就是一种静态常量

枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。–阿里巴巴开发手册

枚举的方法

  • String name(): 返回此枚举常量的名称
  • int ordinal(): 此枚举常量的序号(它在枚举声明中的位置,其中初始常量的序数为零)。
  • String toString: 返回枚举常量名
  • boolean equals(Object other): 判断是否为同一个对象
  • Class<E> getDeclaringClass(): 返回实例所属的 enum 类型
  • static <T extends Enum<T>> T valueOf valueOf(Class<T> enumType String name): 返回指定名字、给定类的枚举常量
  • int compareTo(E other): 如果枚举常量出现在other之前,则返回用一个负值;如果this==other,则返回0;否则,返回正值。枚举常量的出现次数在enum中给出

可以使用== 来比较enum实例

enum的基本方法:

public class EnumMethodDemo {

    public static void main(String[] args) {
        System.out.println("******************Print all color**************");
        for (ColorEnum colorEnum : ColorEnum.values()) {
            System.out.println(colorEnum + " ordinal " + colorEnum.ordinal());
        }

        System.out.println("******************Print method**************");

        ColorEnum black = ColorEnum.BLACK;
        System.out.println("black.name(): " + black.name());
        System.out.println("black.getDeclaringClass(): " + black.getDeclaringClass());
        System.out.println("black.hashCode(): " + black.hashCode());
        System.out.println("black.compareTo(ColorEnum.BLACK): " + black.compareTo(ColorEnum.BLACK));
        System.out.println("black.equals(ColorEnum.BLACK): " + black.equals(ColorEnum.BLACK));
        System.out.println("black.equals(ColorEnum.WHITE): " + black.equals(ColorEnum.WHITE));
        System.out.format("black == ColorEnum.WHITE", black == ColorEnum.WHITE);
    }
}

输出

******************Print all color**************
BLACK ordinal 0
WHITE ordinal 1
GREEN ordinal 2
******************Print method**************
black.name(): BLACK
black.getDeclaringClass(): class com.sun.advanced.learnenum.ColorEnum
black.hashCode(): 1956725890
black.compareTo(ColorEnum.BLACK): 0
black.equals(ColorEnum.BLACK): true
black.equals(ColorEnum.WHITE): false
black == ColorEnum.WHITE
Process finished with exit code 0

枚举的特性

基本特性

如果枚举中没有定义方法,也可以在最后一个实例后面加逗号、分号或什么都不加

枚举可以添加方法

枚举值认为从0开始的有序数值

枚举可以添加普通方法、静态方法、抽象方法、构造方法

Java中不允许使用=为枚举常量赋值。而是添加方法来间接实现显示赋值。

枚举可以实现接口

public interface INumberEnum {

    int getCode();
    String getDescription();
}


public enum  ErrorCodeEnum implements INumberEnum {

    OK(0,"成功"),

    ERROR_A(100,"错误A"),

    ERROR_B(200,"错误B");


    private int code;
    private String description;

    ErrorCodeEnum(int number, String description) {
        this.code = number;
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}

枚举的应用

组织常量

以前,在Java中定义常量都是public static final T A;这样的形式。有了枚举类,你可以将有关联关系的常量组织起来,使代码更加易读、安全,并且还可以使用枚举提供的方法

状态机

我们经常使用switch语句来写状态机。JDK1.7以后,switch已经支持int、charstringenum`类型的参数。这几种类型的参数比较起来,使用枚举的switch更具有可读性

public enum StateEnum {
    GREEN,
    YYELLOW,
    RED;
}


public class StateMachineDemo {

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

    public static String getTrafficInstruct(StateEnum singal){
        String instruct = "信号灯故障";
        switch (singal){
            case RED:
                instruct="红灯停";
                break;
            case GREEN:
                instruct="黄灯请注意";
                break;
            case YYELLOW:
                instruct="绿灯行";
                break;
            default:
                break;
        }
        return instruct;
    }
}

输出

红灯停

错误码

枚举常常用于定义程序错误码。下面使一个简单示例

public enum HttpStatusEnum {

    OK(200,"成功"),
    NOT_FOUND(404,"资源未找到");

    private int code;
    private String msg;

    HttpStatusEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}


public class HttpStateDemo {

    public static void main(String[] args) {
        for (HttpStatusEnum value : HttpStatusEnum.values()) {
            System.out.println(value.getCode() +" "+ value.toString());
        }
    }
}

组织枚举

可以将类型接近的枚举通过接口或类组织起来来,但是一般用接口的方式进行组织。
原因是:Java接口在编译时会自动为enum类型加上public static修饰符;Java类编译时会自动为enum类型加上static修饰符,就是说,在类组织enum,如果你不给他修饰为public,那么之恶能在本包中进行访问

在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。举例来说,假设你想用enum来表示不同类别的食物,同时还希望每个enum元素仍然保持Food类型,那么可以这样实现:

public interface Food {

    enum Appetizer implements Food{
        SALAD,SOUP,SPRING_ROLLS;
    }

    enum MainCourse implements Food{
        LASAGNE,BURRITO,PAD_THAI,
        LENTILS,HUMMOUS,VINDALOO;
    }

    enum Dessert implements Food{
        TIRAMISU,GELATO,FRUIT;
    }

    enum Coffee implements Food{
        BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,
        LATTE,CAPPUCCINO,TEA,HERB_TEA;
    }
}

这些枚举都实现了Food接口,所以可以利用Food对具体实例进行实例化

import static com.sun.advanced.learnenum.Food.*;

public class TypeOfFood {
    public static void main(String[] args) {
        Food food = Appetizer.SALAD;
        food = MainCourse.LASAGNE;
        food = Dessert.GELATO;
        food = Coffee.BLACK_COFFEE;
    }
}

枚举工具类

Java 中提供了两个方便操作enum的工具类EnumSetEnumMap

EnumSet

Set是一种集合,只能向其中添加不重复的对象。当然enum也要求成员都是唯一的,所以enum看起来也具有集合的行为。EnumSet是枚举类型的高性能Set实现。它要求放入它的枚举常量必须属于同一枚举类型

主要接口:

  • noneOf:创建一个具有指定元素类型的空EnumSet
  • allOf:创建一个指定元素类型并包含所有枚举值的EnumSet
  • range: 创建一个包括枚举值中指定范围元素的EnumSet
  • complenmentsOf:初始化集合包括指定集合的补集
  • of:创建一个包括参数中所有元素的EnumSet
  • copyOf:创建一个包含参数容器的所有元素的EnumSet
public class EnumSetDemo {

    public static void main(String[] args) {

        EnumSet<HttpStatusEnum> httpStatusEnums = EnumSet.allOf(HttpStatusEnum.class);
        for (HttpStatusEnum value : httpStatusEnums) {
            System.out.println(value.name());
        }
    }
}

EnumMap

EnumMap是一种特殊的Map,它要求其中的key必须来自一个enum。由于enum本身的限制,所以EnumMap在内部可由数组实现。因此EnumMap的速度很快,我们可以放心的使用enum实例在EnumMap中进行查找操作。不过我们只能将enum的实例作为key来调用put()方法,其他操作与使用一般的Map差不多。

public class EnumMapDemo {

    public static void main(String[] args) {

        EnumMap enumMap = new EnumMap(StateEnum.class);
        enumMap.put(StateEnum.RED,"红灯");
        enumMap.put(StateEnum.GREEN,"绿灯");
        enumMap.put(StateEnum.YYELLOW,"黄灯");

        for (Object o : enumMap.entrySet()) {
            System.out.println(o);
        }
    }
}

参考资料

  • Java编程思想
  • 深入理解Java枚举类
  • 阿里巴巴开发手册