Java基础

引言

如果说泛型是对类型的限制,那么枚举则是对数据范围的限制了

语法

基础语法

public enum WeekDay {
    MON(1,"MONDAY");

    private int code;
    private String desc;
    WeekDay(int code,String desc){
        this.code = code;
        this.desc = desc;
    }
}

可以看到,枚举类相较于普通的类,在成员变量和成员方法上几乎没有区别
只是在定义完了成员变量和方法之后,可以在类定义的最前面提前定义好一些枚举对象
其实这只是一个语法糖,枚举对象其实就类似下面这样(详细内容在实现原理小节中有叙述)

    // public static final WeekDay MON = new WeekDay(1,"MONDAY");
    MON(1,"MONDAY");

定义方法

因为枚举其实就是个普通的类,所以我们可以为它添加方法

public enum WeekDay {
    // public static final WeekDay MONDAY = new WeekDay(1,"周一");
    MON(1,"MONDAY");

    private int code;
    private String desc;
    WeekDay(int code,String desc){
        this.code = code;
        this.desc = desc;
    }
    // 添加方法
    public int getCode() {
        return this.code;
    }
}

常用方法

枚举原生会有一些常用的方法,这里列举一下

name()

枚举方法的名称

WeekDay.MON.name();  // MON

ordinal()

枚举定义的序号,即枚举定义的顺序,从前到后依次递增

WeekDay.MON.ordinal(); // 0

valueOf

根据值返回枚举对象,默认是根据name返回枚举对象

WeekDay.valueOf("MON").getDesc();  // MONDAY

如果你想根据其他的值获取枚举对象的话,可以自己再写一个工具类

values

Enum没有values方法
枚举对象可以调用values方法,但是父类Enum中却没有这个方法,因为这个方法是编译器在编译时给枚举类加上的
所以当你把枚举向上转型为Enum时,你无法使用values方法

WeekDay.values(); 

// 为了方便打印,你可以转成list,另外这里我加了一个周二
Arrays.asList(WeekDay.values());  // [MON,TUE] 

注意:values返回的是原数组的拷贝

getEnumConstants

在上面个的values方法中,我们虽然无法针对父类Enum使用values方法,但是可以使用getEnumConstants方法,因为这个方法属于Class

WeekDay[] weekDays = WeekDay.class.getEnumConstants();
// 同样的,你可以转成List
Arrays.asList(weekDays);  // [MON,TUE]

实现原理

基本原理

枚举其实也只是普通的类,但是要搞清楚它是如何完成数据范围限制的

  1. 枚举就是一个常量类(final的),并且构造方法是私有的,所以你没办法新增更多对象
  2. 枚举对象也是静态final的对象,静态保证对象唯一,final保证对象不会被替换

反编译结果

public final class WeekDay extends Enum<WeekDay> {
    public static final /* enum */ WeekDay MON = new WeekDay("MON", 0, 1, "MONDAY");
    private int code;
    private String desc;
    private static final /* synthetic */ WeekDay[] $VALUES;

    public static WeekDay[] values() {
        return (WeekDay[])$VALUES.clone();
    }

    public static WeekDay valueOf(String name) {
        return Enum.valueOf(WeekDay.class, name);
    }

    private WeekDay(String string, int n, int code, String desc) {
        super(string, n);
        this.code = code;
        this.desc = desc;
    }

    static {
        $VALUES = new WeekDay[]{MON};
    }
}

枚举 + 抽象方法

理解原理后,再来看一种枚举的用法,例如现在我们有这样一个需求:
针对不同的工作类型,计算工资(时薪):

  1. 上班 :工资 * 1
  2. 加班:工资 * 2
  3. 请假:工资 * 0

我们可以像下面这样做:

public enum SalaryCouter {
    WORK_SALARY(1) {
        @Override
        public int countSalary(int baseSalary) {
            return baseSalary * 1;
        }
    },
    OVERTIME_SALARY(2) {
        @Override
        public int countSalary(int baseSalary) {
            return baseSalary * 2;
        }
    },
    LEAVE_SALARY(3) {
        @Override
        public int countSalary(int baseSalary) {
            return 0;
        }
    };
    
    private int code;
    SalaryCouter(int code) {
        this.code = code;
    }

    public abstract int countSalary(int baseSalary);

}

测试一下

        int totalSalary = 8 * SalaryCouter.WORK_SALARY.countSalary(50);

理解原理之后,相信理解这个例子也不困难了,这就相当于是在创建枚举对象时使用了匿名对象的方式

配合客户端

当然了,你这里会吐槽,你这里写死了是哪种工资结算方式,那么前端传过来的时候怎么知道呢?
其实,这里只需要简单的改动下即可

  1. 在枚举类中添加类似valueOf的工具方法
  2. 前端根据工具方法传值即可(例如这里根据code)
  • 添加工具方法
    这里只是简单写了下,你也可以使用map存储code和对应的枚举实例进行缓存
public static SalaryCouter getSalaryCounter(int code) {
        switch (code) {
            case 1:
                return WORK_SALARY;
            case 2:
                return OVERTIME_SALARY;
            case 3:
                return LEAVE_SALARY;
            default:
                return WORK_SALARY;
        }
    }
  • 测试一下
// 这里的1就是前端对应传过来的值
int totalSalary2 = SalaryCouter.getSalaryCounter(1)
                                        .countSalary(50);

小结

这样写的好处是,未来不论增加多少种工资结算的方式,客户端的代码都不需要变动

枚举 vs 常量类

枚举的优点:

  1. 因为是通过对象保存的,所以说可以存储更多信息,特别是需要绑定到一起的信息,例如状态码和状态信息
  2. 枚举 + 抽象方法:可以针对某一个功能(例如计算工资的方式)提供有限的实现方式(枚举无法被继承,你也无法新建一个枚举对象)
原文地址:https://www.cnblogs.com/bax-life/p/14683099.html