美文网首页方案
[Java]重学Java-枚举类

[Java]重学Java-枚举类

作者: AbstractCulture | 来源:发表于2021-06-18 17:02 被阅读0次

    枚举可以做什么

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

    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,现在我们将需求变动一下:

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

    /**
     * 输入工作日,输出工作时间 <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的策略:
    周三大盘震荡
    

    扩展阅读

    《Thinking on Java》基于Java8的版本
    《Effective Java》

    相关文章

      网友评论

        本文标题:[Java]重学Java-枚举类

        本文链接:https://www.haomeiwen.com/subject/duflyltx.html