如何实现可存储无效枚举值并保留原始值的Java枚举/类枚举结构
可扩展枚举值实现方案
Java原生枚举的实例是编译期固定、类加载阶段初始化完成的,运行时无法动态创建新的枚举实例,因此无法直接实现「未知取值保留原始值」的需求。以下两种方案均可完全满足你提出的switch分支判断、合法性校验、保留原始值的要求,可根据JDK版本选择。
方案1:JDK17+ 密封接口(Sealed Interface)实现(推荐)
这是目前该场景的最优实现,完全类型安全,支持编译器分支穷尽性校验,和原生枚举的使用体验几乎无差异。
import lombok.Getter; import lombok.RequiredArgsConstructor; public sealed interface Quality permits Quality.KnownQuality, Quality.UnknownQuality { int getValue(); boolean isValid(); // 已知固定枚举项,和原有枚举定义完全一致 @Getter @RequiredArgsConstructor enum KnownQuality implements Quality { GOOD(1), BAD(2); private final int value; @Override public boolean isValid() { return true; } } // 未知值实现类,存储原始传入的整数值 @Getter @RequiredArgsConstructor final class UnknownQuality implements Quality { private final int value; @Override public boolean isValid() { return false; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof UnknownQuality that)) return false; return value == that.value; } @Override public int hashCode() { return Integer.hashCode(value); } } // 静态工厂方法,替换原有枚举的取值逻辑 static Quality fromValue(int value) { for (KnownQuality known : KnownQuality.values()) { if (known.getValue() == value) { return known; } } return new UnknownQuality(value); } }
使用方式和你预期完全一致:
// 解析TCP报文值 int value = parseTCPObjectForQuality(); Quality quality = Quality.fromValue(value); publishQuality(quality); // 分支判断(JDK21+ 模式匹配switch写法) switch (quality) { case KnownQuality k when k == KnownQuality.GOOD -> // 处理GOOD逻辑 case KnownQuality k when k == KnownQuality.BAD -> // 处理BAD逻辑 case UnknownQuality u -> { // 未知值场景直接取原始值打日志 log.err(u.getValue()); // 传入3则输出3 } } // 合法性校验 if (!quality.isValid()) { log.err(quality.getValue()); }
方案2:低版本JDK兼容实现(类模拟枚举)
如果使用JDK16及以下版本,不支持密封接口,可以用普通类模拟枚举行为,将固定值定义为类的静态常量即可:
import lombok.Getter; @Getter public class Quality { // 固定枚举项 public static final Quality GOOD = new Quality(1, true); public static final Quality BAD = new Quality(2, true); private final int value; private final boolean valid; private Quality(int value, boolean valid) { this.value = value; this.valid = valid; } public boolean isValid() { return valid; } public static Quality fromValue(int value) { if (value == GOOD.value) return GOOD; if (value == BAD.value) return BAD; return new Quality(value, false); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Quality q)) return false; return value == q.value && valid == q.valid; } @Override public int hashCode() { return Integer.hashCode(value); } }
该方案的核心逻辑和密封接口方案一致,仅在低版本JDK中使用switch时需要配合类型判断,JDK17+开启模式匹配后用法和方案1完全相同。
避坑提醒
不要尝试通过反射动态给原生枚举添加实例,也不要通过给枚举加一个UNKNOWN枚举项、再修改其内部字段存未知值的方式实现:枚举是单例实现,全局只有一个UNKNOWN实例,多线程场景下不同未知值会互相覆盖,直接引发逻辑错误。
内容的提问来源于stack exchange,提问作者themagicalyang
相关产品推荐
相关产品推荐

