关于枚举的高级用法
枚举回顾
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 要表达的含义是今天到明天)表达清楚、表达完整。
进阶
🤔以上代码还有什么问题么?例如如何进行单元测试?