Java 枚举
enum 如何表达封闭常量集:类型安全、方法与字段、switch 与序列化边界。
#concept
#growing
#dev
#java
[!info] related notes
Java 枚举
这个概念为什么出现
订单状态、支付渠道、权限级别是封闭且有限的集合。用 int/String 魔法值易拼错;enum 提供类型安全的常量集,并可挂行为。
[!abstract] 一句话理解
enum是类型安全的封闭常量集合,每个常量是单例实例,可拥有字段与方法,并与 switch 良好配合。
最小可运行示例
场景:订单状态允许的合法迁移
enum OrderStatus {
CREATED, PAID, SHIPPED, CANCELLED;
boolean canTransitTo(OrderStatus next) {
return switch (this) {
case CREATED -> next == PAID || next == CANCELLED;
case PAID -> next == SHIPPED || next == CANCELLED;
case SHIPPED, CANCELLED -> false;
};
}
}
public class OrderStatusDemo {
public static void main(String[] args) {
System.out.println(OrderStatus.CREATED.canTransitTo(OrderStatus.PAID));
System.out.println(OrderStatus.SHIPPED.canTransitTo(OrderStatus.PAID));
System.out.println(OrderStatus.PAID.name());
}
}
建议运行:
javac OrderStatusDemo.java && java OrderStatusDemo
期望输出:
true
false
PAID
结合场景再看三个关注点
- 比较用
==即可(单例常量)。 - 状态机规则可放 enum 方法,避免散落 if。
- valueOf 按声明名精确匹配且区分大小写;非法名抛 IllegalArgumentException,null 抛 NPE,外部输入需明确解析策略。
核心概念与准确模型
1. 本质
- enum 隐式继承 Enum。没有常量特定类体时隐式 final;有这类常量体时隐式 sealed,其允许子类是对应的匿名常量子类。\
- 常量对应 public static final 字段,实例身份稳定;枚举自身的实例字段仍可变,不自动获得线程安全。\
2. 可扩展
- 字段、构造器(private)、方法
- 实现接口
- 常量体覆盖方法(常量特定策略)
3. 常用 API
name()/ordinal()(ordinal 不适合持久化稳定契约)values()/valueOf
边界情况与反直觉行为
- 持久化存 ordinal 后插入新常量会错位——存 name 或稳定 code 字段。
- Java 对象序列化按枚举声明名恢复常量,重命名会影响兼容性;JSON 映射是另一套库契约。name 稳于 ordinal,但也不是可随意改名的永久标识。\
- switch 表达式要求穷尽,传统 enum switch 语句不一定要求;增加常量后需重新编译并检查分支,不能把源代码穷尽检查当作所有版本组合的保证。
- name 返回声明名,toString 可被覆写;常量特定类体可能使 getClass 不同,判断枚举声明类型用 getDeclaringClass。\
常见误区
[!warning] 常见误区:用 ordinal 当数据库主键 顺序变化即数据腐蚀;使用显式 code。
工程实践
- 业务状态用 enum + 明确 code。
- 复杂状态机可用独立表/类,enum 保常量。
- JSON 映射配置枚举格式。
- 权限点等可用 enum 集合。
本节总结
- enum = 类型安全封闭集
- 可承载行为
- 持久化用稳定标识而非 ordinal
自测题
- 为什么枚举常量可以用
==比较? ordinal()的风险是什么?
参考答案
- 每个常量是唯一实例(语言保证的枚举单例语义)。
- 声明顺序变化导致数值变化,不适合作为稳定对外契约。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Enum Types (Oracle Tutorial) | 教程 | enum |
| JLS Enum Classes | 规范 | 枚举类 |