场景一
之前一个刚毕业参加工作的网友问我一个问题,想对一个集合排序
如上图,想根据 checkLineLocation 字段值排序这个集合。我毕竟已经工作一年多了,经验相对比他多,首先肯定要问排序规则是什么,总不可能使用 String 默认的字典排序。
他说按照这个图从左到右的顺序,左边墙、左拱腰、拱顶、右拱腰、右边墙。现在我们知道了排序规则,但是 checkLineLocation 是 String 类型的,有自己默认的排序规则,所以我们需要给这个字段建立一个映射关系,然后写另一个排序规则。那么我首先想到的就是使用枚举。
首先定义一个枚举类
public enum LineLocationEnum {
LOCATION_LEFT_WALL( "左边墙",1),
LOCATION_LEFT_WAIST("左拱腰",2),
LOCATION_TOP("拱顶",3),
LOCATION_RIGHT_WAIST("右拱腰",4),
LOCATION_RIGHT_WALL("右边墙",5);
LineLocationEnum(String location,int sort){
this.location = location;
this.sort = sort;
}
@Getter
private final String location;
@Getter
private final int sort;
//根据位置获取排序值
public static int getSortByLocation(String location) {
return Arrays.stream(LineLocationEnum.values())
.filter(orderStatusEnum -> orderStatusEnum.getLocation().equals(location))
.map(LineLocationEnum::getSort)
.findFirst()
.orElse(0);
}
}
这样我们就给 checkLineLocation 关联上了排序规则 sort,并且提供了根据 location 获取排序值 sort 的方法 getSortByLocation。然后写代码测试
public static void main(String[] args) {
List<CheckDataReport> list = new ArrayList<CheckDataReport>(){{
add(new CheckDataReport("拱顶"));
add(new CheckDataReport("右边墙"));
add(new CheckDataReport("左拱腰"));
add(new CheckDataReport("左边墙"));
add(new CheckDataReport("右拱腰"));
}
};
//排序
list.sort(Comparator.comparingInt(o -> LineLocationEnum.getSortByLocation(o.getCheckLineLocation())));
System.out.println(list);
}
这样排序问题就解决了,先使字段可以按照我们的规则排序,然后调用集合的 sort 方法,在实现的 comparator 接口匿名类中,使用我们的排序规则即可。
枚举的好处在于它可以使一些数字符号化,然后增强程序的可读性。 比如上述问题,我们也可以在程序中 new 一个 HashMap 存储对应的 sort 排序值,然后排序,但是代码繁多,可读性差。使用枚举代码量很少,而且定义好枚举,下次类似场景还可以复用。
场景二
工作中某些字段在数据库中存储的可能是数字代表具体中文描述,比如订单状态。
10:待支付,15:待支付尾款 20:待发货,30:待收货,40:已完成,50:已关闭,60退款中,70已退款
如果说是 web 列表,我们返回数字给前端也没问题,swagger 接口文档里面注释好即可
@Schema(description = "子订单状态,10:待支付,20:待发货,30:待收货,40:已完成,50:已关闭,60:退款中,70:已退款")
private Integer childOrderStatus;
但是有一种场景是 excel 导出,这个功能是和前端没什么关系的,后端直接就把 excel 的字节流返回给浏览器了,所以我们需要直接返回对应的描述。此时也可以使用枚举。
@RequiredArgsConstructor
public enum OrderStatusEnum {
WAIT_PAY(10, "待支付"),
//...其他状态
public static String getDescByCode(Integer code) {
return Arrays.stream(OrderStatusEnum.values())
.filter(orderStatusEnum -> orderStatusEnum.getCode().equals(code))
.map(OrderStatusEnum::getDesc)
.findFirst()
.orElse(StringUtils.EMPTY);
}
}
在组装 excel 返回实体类中直接使用 getDescByCode 拿到对应的订单状态描述即可。当然我们也可以不使用枚举,在设置订单状态的时候,在方法内使用 switch 结构判断一下也行
switch (status) {
case 10:
//...
break;
case 15:
//...
break;
case 20:
//...
这样代码量繁多,而且可读性没有使用枚举高,并且下次在其他方法内部还需要写这种代码。
封装工具类
你可能已经发现了,上面的 getDescByCode 方法是写在枚举类里面的,正常项目中枚举少说十几二十个吧,如果每个类都写这么一段重复代码,这就违背了 DRY(Dont Repeat Yourself)原则。所以我们可以封装一个工具类,先定义枚举接口
public interface GenericEnum {
Integer getCode();
String getDesc();
}
然后我们的枚举类实现这个接口即可,结合我们上篇说过的泛型 一文带你搞懂 Java 泛型 ,定义泛型方法
public final class EnumUtils {
/***
* 根据code获取枚举
*/
public static <T extends GenericEnum> T getEnumByCode(Integer code, Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(t -> t.getCode().equals(code))
.findFirst().orElse(null);
}
/***
* 根据code获取描述
*/
public static <T extends GenericEnum> String getEnumDescByCode(Integer code, Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(t -> t.getCode().equals(code))
.map(T::getDesc).findFirst()
.orElse(null);
}
}
结语
枚举适合的场景就是定义一组有限的集合,像颜色、状态、优惠券类型、快递运输状态等等都适合使用枚举。本篇文章列举了两个博主使用枚举的场景,以后有其他使用场景会继续更新过来。