今天聊一下Java枚举

枚举可以做什么

假设我们现在想表达星期,为了避免魔法值的出现,我们可能会写一个常量类:

package com.tea.modules.java8.enums;

/**

 * com.tea.modules.java8.enums <br>
 * 使用抽象类来表达常量,防止被篡改
 * @author jaymin
 * @since 2021/6/9
 */
public abstract class WeekConstant {
    /**
     * 周一
     */
    public static final Integer MON = 1;
    /**
     * 周二
     */
    public static final Integer TUE = 2;
    /**
     * 周三
     */
    public static final Integer WEB = 3;
    /**
     * 周四
     */
    public static final Integer THUR = 4;
    /**
     * 周五
     */
    public static final Integer FRI = 5;
    /**
     * 周六
     */
    public static final Integer SAT = 6;
    /**
     * 周日
     */
    public static final Integer SUN = 7;
}

这样一来,1-7的星期数就可以用WeekConstant.MON这样的方式来表达了,好看了不少,但是仍然存在一定的缺陷。

假设我们现在往WeekConstant中再添加一行:

public static final Integer WEEK = 1;

将其传入到一个接收参数为”星期一”的方法中,并不会产生任何问题,因为都能表示1,但是从代码阅读上来看,出现歧义了。

JDK提供了Enum枚举类,让代码可读性更强,同时它也是类型安全的类,所有的枚举类都被final修饰,还可以很好地实现单例模式.

枚举类基础用法

定义一个枚举类

  • Week
package com.tea.modules.java8.enums;

/**

 * com.tea.modules.java8.enums <br>
 * 使用枚举类来表示星期
 *
 * @author jaymin
 * @since 2021/6/9
 */
public enum WeekEnum {
    /**
     * 周一
     */
    MON,
    /**
     * 周二
     */
    TUE,
    /**
     * 周三
     */
    WEB,
    /**
     * 周四
     */
    THUR,
    /**
     * 周五
     */
    FRI,
    /**
     * 周六
     */
    SAT,
    /**
     * 周日
     */
    SUN;
}

  • Demo
package com.tea.modules.java8.enums;

/**

 * com.tea.modules.java8.enums <br>
 * 实战枚举类
 *
 * @author jaymin
 * @since 2021/6/9
 */
public class EnumDemo {
    /**
     * 了解枚举的基本功能
     */
    private static void enumFunction() {
        // 获取所有的枚举信息
        WeekEnum[] weekDays = WeekEnum.values();
        for (WeekEnum weekDay : weekDays) {
            String enumName = "当前枚举名称:" + weekDay.name();
            String enumIndex = " 枚举位置:" + weekDay.ordinal();
            String enumClass = " 枚举类型:" + weekDay.getDeclaringClass();
            System.out.println(enumName + enumIndex + enumClass);
        }
        WeekEnum mon = WeekEnum.valueOf("MON");
        System.out.println(mon.name());
        WeekEnum monB = Enum.valueOf(WeekEnum.class, "MON");
        System.out.println(monB);
    }

    public static void main(String[] args) {

        enumFunction();

    }

}

输出结果:

当前枚举名称:MON 枚举位置:0 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:TUE 枚举位置:1 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:WEB 枚举位置:2 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:THUR 枚举位置:3 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:FRI 枚举位置:4 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:SAT 枚举位置:5 枚举类型:class com.tea.modules.java8.enums.WeekEnum

当前枚举名称:SUN 枚举位置:6 枚举类型:class com.tea.modules.java8.enums.WeekEnum

MON

MON

API Document

API

描述

name

枚举的名字

Enum.values()

返回一个当前枚举类中的所有枚举元素

ordinal

返回当前枚举在枚举类中的索引,从0开始

getDeclaringClass

返回枚举类的类型

Enum.valueOf()

返回与传入的名称相等的枚举,可能会抛出异常

switch配合枚举类编写状态机

OK,现在我们将需求变动一下:

周一到周五是工作日,输出工作时间;undefined 周六周日是休息日,输出“休息”;

/**

 * 输入工作日,输出工作时间 <br>
 * 枚举的本质就是int,配合switch的时候,编译会做类似于ordinal来确定int值
 */
private static void printWorkDays(WeekEnum weekEnum) {
    switch (weekEnum) {
        case MON:
            System.out.println("今天是工作日-8:30-5:30");
            break;
        case TUE:
            System.out.println("今天是工作日-8:30-5:30");
            break;
        case WEB:
            System.out.println("今天是工作日-8:30-5:30");
            break;
        case THUR:
            System.out.println("今天是工作日-8:30-5:30");
            break;
        case FRI:
            System.out.println("今天是工作日-8:30-5:30");
            break;
        default:
            System.out.println("休息");
    }
}

可读性得到了提高,如果你看到代码里面有人写:case 1这种代码,请提醒他可以使用枚举增加可读性.

使用抽象方法为每个枚举指定特定的行为

枚举不仅可以用来表示常量,有些时候,我们也可以将一些简单的计算逻辑写在枚举类中。这个时候,可以使用抽象方法来定义每个枚举需要实现的行为.

package com.tea.modules.java8.enums;

import lombok.Getter;

/**

 * com.tea.modules.java8.enums <br>
 * 运算符枚举
 *
 * @author jaymin
 * @since 2021/6/10
 */
@Getter
public enum OperationEnum {
    /**
     * 加
     */
    PLUS("+") {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },
    /**
     * 减
     */
    MINUS("-") {
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    },
    /**
     * 乘
     */
    TIMES("*") {
        @Override
        public double apply(double x, double y) {
            return x * y;
        }
    },
    /**
     * 除
     */
    DIVIDE("/") {
        @Override
        public double apply(double x, double y) {
            return x / y;
        }
    };

    /**

     * 运算符
     */
    private final String symbol;

    OperationEnum(String symbol) {

        this.symbol = symbol;

    }

    public abstract double apply(double x, double y);

}

接口与枚举类

枚举类是final类,不支持继承关系。这个可以从反编译的文件中查看:

反编译

可以通过接口来让枚举实现一些通用的方法

  • 定义一个接口,声明打印名字的能力
package com.tea.modules.java8.enums;

/**

 * com.tea.modules.java8.enums
 * 枚举接口
 * @author jaymin
 * @since 2021/6/17
 */
public interface EnumInfoService {
    /**
     * 打印枚举的名字
     */
    void printName();
}

  • 枚举类实现接口
package com.tea.modules.java8.enums;

/**

 * com.tea.modules.java8.enums <br>
 * 使用枚举类来表示星期
 *
 * @author jaymin
 * @since 2021/6/9
 */
public enum WeekEnum implements EnumInfoService{
    /**
     * 周一
     */
    MON,
    /**
     * 周二
     */
    TUE,
    /**
     * 周三
     */
    WEB,
    /**
     * 周四
     */
    THUR,
    /**
     * 周五
     */
    FRI,
    /**
     * 周六
     */
    SAT,
    /**
     * 周日
     */
    SUN;

    @Override

    public void printName() {

        System.out.println(this.name());

    }

}

  • Demo
/**

 * 枚举与接口
 */
private static void printEnumInfo(){
    EnumInfoService web = WeekEnum.WEB;
    web.printName();
}
public static void main(String[] args) {
    printEnumInfo();
}

  • Result
WEB

EnumSet

EnumSet可以存储一个枚举中的元素,它提供了一种以集合的方式去操作枚举类的途径。

  • enumSet
    /**

     * EnumSet 的设计充分考虑到了速度因素,因为它必须与非常高效的 bit 标志相竞争(其操作与 HashSet 相比,非常地快).<br>
     * 就其内部而言,它(可能)就是将一个 long 值作为比特向量,所以 EnumSet 非常快速高效。<br>
     * 使用 EnumSet 的优点是,它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能。
     */
    private static void enumSet() {
        // 空构造器
        EnumSet<WeekEnum> weekEnums = EnumSet.noneOf(WeekEnum.class);
        weekEnums.add(WeekEnum.MON);
        weekEnums.add(WeekEnum.TUE);
        weekEnums.add(WeekEnum.WEB);
        // of工厂,可以接收多个enum
        weekEnums.addAll(EnumSet.of(WeekEnum.THUR, WeekEnum.FRI, WeekEnum.SAT, WeekEnum.SUN));
        System.out.println(weekEnums);
        // range-范围
        weekEnums.removeAll(EnumSet.range(WeekEnum.MON, WeekEnum.WEB));
        System.out.println(weekEnums);
        // 创建一个与指定枚举集具有相同元素类型的枚举集,最初包含指定集中未包含的所有此类型的元素。
        weekEnums = EnumSet.complementOf(weekEnums);
        System.out.println(weekEnums);
    }

  • Result
[MON, TUE, WEB, THUR, FRI, SAT, SUN]

[THUR, FRI, SAT, SUN]

[MON, TUE, WEB]

EnumMap

EnumMap可以以枚举作为Key,提供一种更快的速度来访问Map.

  • enumMap
    /**

     * EnumMap 是一种特殊的 Map,它要求其中的键(key)必须来自一个 enum<br>
     * 由于 enum 本身的限制,所以 EnumMap 在内部可由数组实现。<br>
     * 因此 EnumMap 的速度很快,我们可以放心地使用 enum 实例在 EnumMap 中进行查找操作。<br>
     * 不过,我们只能将 enum 的实例作为键来调用 put() 可方法,其他操作与使用一般的 Map 差不多。<br>
     * 这里我们的例子是使用EnumMap来实现一个简单的命令模式
     */
    private static void enumMap() {
        EnumMap<WeekEnum, CommandService> commandServiceEnumMap = new EnumMap<>(WeekEnum.class);
        commandServiceEnumMap.put(WeekEnum.MON, () -> System.out.println("周一白酒涨停"));
        commandServiceEnumMap.put(WeekEnum.TUE, () -> System.out.println("周二有色跌停"));
        commandServiceEnumMap.put(WeekEnum.WEB, () -> System.out.println("周三大盘震荡"));
        for (Map.Entry<WeekEnum, CommandService> weekEnumCommandService : commandServiceEnumMap.entrySet()) {
            WeekEnum week = weekEnumCommandService.getKey();
            System.out.println(week.name() + "的策略:");
            weekEnumCommandService.getValue().action();
        }
    }

  • Result
MON的策略:

周一白酒涨停

TUE的策略:

周二有色跌停

WEB的策略:

周三大盘震荡

正文完