目录
引言
如果说泛型是对类型的限制,那么枚举则是对数据范围的限制了
语法
基础语法
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]
实现原理
基本原理
枚举其实也只是普通的类,但是要搞清楚它是如何完成数据范围限制的
- 枚举就是一个常量类(final的),并且构造方法是私有的,所以你没办法新增更多对象
- 枚举对象也是静态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
- 加班:工资 * 2
- 请假:工资 * 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);
理解原理之后,相信理解这个例子也不困难了,这就相当于是在创建枚举对象时使用了匿名对象的方式
配合客户端
当然了,你这里会吐槽,你这里写死了是哪种工资结算方式,那么前端传过来的时候怎么知道呢?
其实,这里只需要简单的改动下即可
- 在枚举类中添加类似valueOf的工具方法
- 前端根据工具方法传值即可(例如这里根据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 常量类
枚举的优点:
- 因为是通过对象保存的,所以说可以存储更多信息,特别是需要绑定到一起的信息,例如状态码和状态信息
- 枚举 + 抽象方法:可以针对某一个功能(例如计算工资的方式)提供有限的实现方式(枚举无法被继承,你也无法新建一个枚举对象)