关于枚举的高级用法
枚举回顾
Java 中的枚举,大家都不会陌生,他的一些特点,能让我们得心应手的实现业务逻辑。
- 它的单例特性,我们就可以直接使用
==
来做逻辑校验,如:
1 2 3 4 5 6 7
| public enum Gender { MAN, WOMAN; } if (Gender.MAN == gender) { }
|
- 也可以直接实现一个线程单例,JVM 层面提供的保障
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class SingleEnumDemo { public enum SingleEn {
INSTANCE;
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; } } public static void main(String[] args) { SingleEn.INSTANCE.setName("zp"); System.out.println(SingleEn.INSTANCE.getName()); } }
|
1 2 3 4 5 6 7
| public enum Gender { MAN, WOMAN; }
if (null == Gender.valueOf("xxxxx")) { throw new IllegalArgumentException(); }
|
- 当然最常见的,还是定义各种状态,似乎觉得枚举天生就是这样用的
1 2 3 4 5 6 7 8 9 10 11
| @Getter @AllArgsConstructor public enum ShareRuleEnum { REDPACKET("店鋪紅包", 1), MANJIAN("滿減優惠", 2), MANZENG("滿贈活動", 4), ZHEKOU("折扣商品", 8), ; private String shareRuleName; private Integer code; }
|
枚举使用的其他案例
某个场景,需要一个查询的接口,查询的参数有时间区间,即开始时间~结束时间。自然我们会想到,定义两个请求参数 start
、end
1 2 3 4 5
| public MsgResult<XXXResult> get( @RequestParam Date start, @RequestParam Date end ) { }
|
在这个场景上,附加上了一个规则,只能使用给定的时间区间(如:今天,昨天,本月,上月…),这时大家应该能想到枚举又有用武之地了,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public MsgResult<XXXResult> get( @RequestParam DateRangeEnum dateRange ) { Date start, end; if (dateRange == TODAY) { start = ..., end = ...; } ... }
enum DateRangeEnum { TODAY, YESTEDAY, CURRENT_MONTH, LAST_MONTH; }
|
等等……
思考一下,我们为什么要定义枚举?
是为了要约束入参的范围,这个枚举确实做到了部分,因为只能传递 TODAY
,YESTEDAY
, CURRENT_MONTH
, LAST_MONTH
。
但这些枚举值所代表的含义是什么呢?例如 TODAY
是希望 start
为今天的开始,end
为明天的开始(闭区间)
然而这些代码却没有在 DateRangeEnum
类里面体现,而写在了其他方法里面。
由此我们得出结论,我们提供的 DateRangeEnum
类表达的意思不全的,是残缺的,最起码现在看来残缺了时间区间的表达。
改进一版,提供一个时间区间的接口(面向接口编程),以及一个普通 Bean 的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface DateRange { Date getStart(); Date getEnd(); }
@Data @AllArgsConstructor @NoArgsConstructor class DateRangeParam implements DateRange { private Date start; private Date end; }
|
改造枚举 DateRangeEnum
, 让他能够产生时间区间,我们就利用 Java8 已经定义好了接口 Supplier
,让 DateRangeEnum
实现该接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public MsgResult<XXXResult> get( @RequestParam DateRangeEnum dateRange ) { DateRange range = dateRange.get(); range.getStart(); range.getEnd(); ... }
enum DateRangeEnum implements Supplier<DateRange> { TODAY { @Override public DateRange get() { Date now = new Date(); return new DateRangeParam( truncate(now, Calendar.DATE), addDays(truncate(now, Calendar.DATE), 1) ); } }, YESTERDAY { @Override public DateRange get() { Date now = new Date(); return new DateRangeParam( addDays(truncate(now, Calendar.DATE), -1), truncate(now, Calendar.DATE) ); } }, ; }
|
这样,枚举中就包含了时间区间的含义了,通过调用枚举的 get
方法,如 TODAY.get()
,就能获取时间区间 DateRange
了。
这就是枚举的高级用法,『高级』其实指的不是技术上的高级,无非是实现了接口而已,而是把枚举需要表达的含义(如 TODAY
要表达的含义是今天到明天)表达清楚、表达完整。
进阶
🤔以上代码还有什么问题么?例如如何进行单元测试?