枚举的常用姿势 (JAVA 为例)

几乎没啥用的示例

初见枚举,见过最多的实例无外乎有两个 ColorWeek,拿其中一个举个例子看看

public enum Color {
    GREEN,
    YELLOW,
    RED
}

然后呢?怎么用?if/else ? switch ?

这样用的话,跟常量类唯一的区别就是限定了类型,让编译器能够发力限制使用以此枚举为入参的 api 参数类型,在一定程度上是一种进步,但实际使用完全没有变化。

初级进化版本

我们用的数据通常是要持久化的,持久化一般用数字或者某些字符串来代替其实际含义,这样来看上面那个枚举几乎没啥用了。

// 使用了 lombok 的注解,简化代码
@Getter
@AllArgsConstructor
public enum Color {
    GREEN(1),
    YELLOW(2),
    RED(3);
    
    /**
     * 持久化的 code 值
     */
    private Integer code;
}

现在持久化的数据和枚举建立联系了,咋个用呢?貌似还是 switch 只是多了一个 getCode() 的方法能拿到对应持久化的值,对于要通过枚举值获得持久化的值,到这里就可以做到了。

可是我们从前端拿到的值是持久化的代表值,咋个转化成枚举呢?也就是从 code -> enum 这步该怎么搞呢?

再进化版本

// 使用了 lombok 的注解,简化代码
@Getter
@AllArgsConstructor
public enum Color {
    GREEN(1),
    YELLOW(2),
    RED(3);
    
    private Integer code;
    
    /**
     * 表驱动
     * CODE: 枚举实例
     * 
     * 不要直接暴露这个 Map 出去,提供一个方法出去,限制对 Map 的操作
     */
    private static final Map<Integer, Color> CODE_TO_ENUM =
            Arrays.stream(Color.values())
                    .collect(Collectors.toMap(Color::getCode, e -> e));
    
    /**
     * 通过 code 获取枚举实例
     * 这里使用了 Optional 语义上表示如果 code 不存在(非法值),起一个提示作用
     * 
     * @param code code
     * @return 枚举实例
     */
    public static Optional<Color> forCode(Integer code) {
        return Optional.ofNullable(CODE_TO_ENUM.get(code));
    }
}

到这里,这个枚举就比较能覆盖常见使用场景了,比如,前台选中某种颜色,传到后端

后端的 xxxService.process(Color color) 只接受 Color 类型,在这一层就限制了入参的范围,让 API 更健壮。

Optional<Color> colorOpt = Color.forCode(colorCode);
if (colorOpt.isPresent()) {
    return xxxService.process(colorOpt.get());
}
return "something else";

终极进化

一般上面的版本就够用了,但是如果后面对于不同的 Color 要做除了 getCode() 以外的操作,还是逃不脱 if/elseswitch 这两种做法,那该怎么解决呢?答案是抽象方法。

是的,在枚举里是可以定义抽象方法的,各个实例去实现这个方法就可以了,代码可以参考 JDK8 的 java.util.concurrent.TimeUnit#toXxx() 方法

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Getter
@AllArgsConstructor
public enum Color {
    GREEN(1) {
        @Override
        public void printColor() {
            System.out.println("绿色");
        }
    },
    YELLOW(2) {
        @Override
        public void printColor() {
            System.out.println("黄色");

        }
    },
    RED(3) {
        @Override
        public void printColor() {
            System.out.println("红色");
        }
    };

    private Integer code;

    /**
     * CODE: 枚举实例
     */
    private static final Map<Integer, Color> CODE_TO_ENUM =
            Arrays.stream(Color.values())
                    .collect(Collectors.toMap(Color::getCode, e -> e));

    /**
     * 通过 code 获取枚举实例
     * 
     * 这里使用了 Optional 语义上表示如果 code 不存在(非法值),起一个提示作用
     *
     * @param code code
     * @return 枚举实例
     */
    public static Optional<Color> forCode(Integer code) {
        return Optional.ofNullable(CODE_TO_ENUM.get(code));
    }

    /**
     * 打印颜色
     */
    public abstract void printColor();
}

先来看下原始的 switch 没有用抽象方法的使用方式

Optional<Color> colorOpt = forCode(code);
if (colorOpt.isPresent()) {
    Color color = colorOpt.get();
    switch (color) {
        case GREEN:
            System.out.println("绿色");
            break;
        case YELLOW:
            System.out.println("黄色");
            break;
        case RED:
            System.out.println("红色");
            break;
        default:
            break;
    }
}

使用抽象方法,如果要做 printColor() 的动作

Optional<Color> colorOpt = Color.forCode(colorCode);
if (colorOpt.isPresent()) {
    // 这里体现了多态,根据具体实例调用具体方法
    colorOpt.get().printColor();
}

可以看到,逻辑上是一样的,使用抽象方法的这种 主逻辑 更清晰了,做了什么动作一目了然,没有被淹没在各种不同情况的实现细节上面,如果开始只有一个动作用 switch 也没啥,但要是对于不同的枚举值有很多不同的动作,那每做一个动作都去 switch 就真的要人命了。

要注意一点的是,代码是一行也没有少写的,该有的逻辑都有,只有部分交给了程序自己去判断。

总结

  1. 内部以 code 对应枚举实例的 Map 为表,提供通过 code 获取实例的方法;
  2. 定义抽象方法,实例去实现抽象方法,利用多态做分支判断。

预留问题:现在的 code 是一个确定值,从 Map 往外拿值有就是有,没有就是没有,那如果 code 是范围值呢?就像考试成绩的 A,B,C,D 它们是区间分段的,怎么搞呢?

见下一篇 用枚举值表示范围

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lixifun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值