Java 枚举的进阶用法:枚举实现接口、枚举策略模式

Java 枚举不只是简单的常量集合。配合接口和策略模式,枚举可以成为实现复杂业务逻辑的强大工具。本文介绍枚举的各种进阶用法。

枚举基本特性回顾

先回顾枚举的基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum Status {
DRAFT, // 草稿
PUBLISHED, // 已发布
ARCHIVED; // 归档

// 枚举可以包含方法
public boolean isActive() {
return this == PUBLISHED;
}
}

// 使用
Status status = Status.PUBLISHED;
status.isActive(); // true

枚举的本质:

  • public final class Status extends Enum
  • 每个枚举值是该类的静态常量实例
  • 构造函数私有,不能直接 new

枚举实现接口

枚举可以实现接口,这在某些场景下非常有用。

场景:统一处理多种支付方式

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 定义支付接口
public interface Payment {
PayResult pay(Order order);
RefundResult refund(Order order, BigDecimal amount);
}

// 支付宝实现
public class AlipayPayment implements Payment {
@Override
public PayResult pay(Order order) {
// 支付宝支付逻辑
return new PayResult(true, "ali_" + order.getId());
}

@Override
public RefundResult refund(Order order, BigDecimal amount) {
// 退款逻辑
return new RefundResult(true, "ali_refund_" + order.getId());
}
}

// 微信支付实现
public class WechatPayment implements Payment {
@Override
public PayResult pay(Order order) {
return new PayResult(true, "wx_" + order.getId());
}

@Override
public RefundResult refund(Order order, BigDecimal amount) {
return new RefundResult(true, "wx_refund_" + order.getId());
}
}

// 使用枚举统一管理
public enum PaymentType implements Payment {
ALIPAY {
@Override
public PayResult pay(Order order) {
// 支付宝特定逻辑
return new PayResult(true, "ali_" + order.getId());
}
},

WECHAT {
@Override
public PayResult pay(Order order) {
// 微信支付特定逻辑
return new PayResult(true, "wx_" + order.getId());
}
},

UNION_PAY {
@Override
public PayResult pay(Order order) {
return new PayResult(true, "union_" + order.getId());
}
};

// 通用逻辑可以放在枚举类层面
public boolean validate(Order order) {
return order != null && order.getAmount().compareTo(BigDecimal.ZERO) > 0;
}
}

// 调用
public class OrderService {

public void pay(Long orderId, PaymentType paymentType) {
Order order = orderMapper.selectById(orderId);

if (!paymentType.validate(order)) {
throw new BusinessException("订单无效");
}

PayResult result = paymentType.pay(order);
// 处理结果
}
}

接口方法默认实现

如果枚举实现接口时想提供默认实现:

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
36
37
38
39
40
41
42
public interface Validator {
boolean validate(Object input);

// Java 8 default 方法
default String getErrorMessage() {
return "验证失败";
}
}

public enum InputType implements Validator {
EMAIL {
@Override
public boolean validate(Object input) {
if (!(input instanceof String)) return false;
String email = (String) input;
return email.contains("@") && email.contains(".");
}
},

PHONE {
@Override
public boolean validate(Object input) {
if (!(input instanceof String)) return false;
String phone = (String) input;
return phone.matches("1[3-9]\\d{9}");
}
},

ID_CARD {
@Override
public boolean validate(Object input) {
// 身份证验证逻辑
return true;
}
};

// 提供默认实现,覆盖枚举值的行为
@Override
public String getErrorMessage() {
return "格式不正确";
}
}

枚举与策略模式结合

枚举天然适合实现策略模式。

经典策略模式重构

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 策略接口
public interface DiscountStrategy {
BigDecimal calculate(Order order);
}

// 策略实现
public class NoDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(Order order) {
return BigDecimal.ZERO;
}
}

public class PercentageDiscountStrategy implements DiscountStrategy {
private final BigDecimal percentage;

public PercentageDiscountStrategy(BigDecimal percentage) {
this.percentage = percentage;
}

@Override
public BigDecimal calculate(Order order) {
return order.getAmount().multiply(percentage);
}
}

public class FixedAmountDiscountStrategy implements DiscountStrategy {
private final BigDecimal amount;

@Override
public BigDecimal calculate(Order order) {
return amount.min(order.getAmount());
}
}

// 使用枚举替代策略工厂
public enum DiscountType {
NONE {
@Override
public BigDecimal calculate(Order order) {
return BigDecimal.ZERO;
}
},

PERCENTAGE_10 {
@Override
public BigDecimal calculate(Order order) {
return order.getAmount().multiply(new BigDecimal("0.10"));
}
},

PERCENTAGE_20 {
@Override
public BigDecimal calculate(Order order) {
return order.getAmount().multiply(new BigDecimal("0.20"));
}
},

FIXED_100 {
@Override
public BigDecimal calculate(Order order) {
return new BigDecimal("100").min(order.getAmount());
}
},

FIXED_500 {
@Override
public BigDecimal calculate(Order order) {
return new BigDecimal("500").min(order.getAmount());
}
};

public abstract BigDecimal calculate(Order order);
}

// 调用
public class OrderService {

public BigDecimal calculateDiscount(Order order, DiscountType discountType) {
return discountType.calculate(order);
}
}

带参数的策略

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
36
37
38
39
40
public enum TaxType {
VAT(0.06) {
@Override
public BigDecimal calculate(BigDecimal amount) {
return amount.multiply(new BigDecimal(String.valueOf(rate)));
}
},

BUSINESS(0.03) {
@Override
public BigDecimal calculate(BigDecimal amount) {
return amount.multiply(new BigDecimal(String.valueOf(rate)));
}
},

CUSTOM(null) {
@Override
public BigDecimal calculate(BigDecimal amount) {
// 需要外部提供税率
throw new UnsupportedOperationException("CUSTOM 类型需要指定税率");
}

@Override
public BigDecimal calculate(BigDecimal amount, BigDecimal customRate) {
return amount.multiply(customRate);
}
};

private final Double rate;

TaxType(Double rate) {
this.rate = rate;
}

public abstract BigDecimal calculate(BigDecimal amount);

public BigDecimal calculate(BigDecimal amount, BigDecimal customRate) {
throw new UnsupportedOperationException("该类型不支持自定义税率");
}
}

枚举别名与扩展

有时候需要为枚举值设置别名或扩展信息。

扩展信息

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
36
37
38
39
public enum OrderStatus {
DRAFT("draft", "草稿", "可以编辑"),
PENDING("pending", "待支付", "等待用户付款"),
PAID("paid", "已支付", "支付成功,待发货"),
SHIPPED("shipped", "已发货", "商品已发货"),
COMPLETED("completed", "已完成", "订单已完成"),
CANCELLED("cancelled", "已取消", "订单已取消");

private final String code;
private final String name;
private final String description;

OrderStatus(String code, String name, String description) {
this.code = code;
this.name = name;
this.description = description;
}

// 根据 code 查找
public static OrderStatus fromCode(String code) {
for (OrderStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Unknown code: " + code);
}

// 判断是否可转换
public boolean canTransitionTo(OrderStatus target) {
// 状态机规则
return this.canTransitionTo == target;
}

// 获取描述
public String getDescription() {
return description;
}
}

别名支持

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
public enum DeviceType {
MOBILE("mobile", "移动端"),
TABLET("tablet", "平板"),
PC("pc", "电脑"),
// 别名
PHONE("mobile", "手机"); // 与 MOBILE 相同的行为

private final String code;
private final String name;

DeviceType(String code, String name) {
this.code = code;
this.name = name;
}

// 支持按别名查询
private static final Map<String, DeviceType> NAME_MAP = new HashMap<>();

static {
for (DeviceType type : values()) {
NAME_MAP.put(type.code, type);
}
}

public static DeviceType fromCode(String code) {
DeviceType type = NAME_MAP.get(code);
if (type == null) {
throw new IllegalArgumentException("Unknown code: " + code);
}
return type;
}
}

枚举序列化处理

枚举序列化需要注意前后端交互场景。

Jackson 序列化

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
public enum Status {
ACTIVE("active", "激活"),
INACTIVE("inactive", "未激活");

private final String code;
private final String label;

Status(String code, String label) {
this.code = code;
this.label = label;
}

@JsonValue // 序列化时输出 code
public String getCode() {
return code;
}

@JsonCreator // 反序列化时从 code 创建
public static Status fromCode(String code) {
for (Status status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Unknown status: " + code);
}
}

使用效果:

1
2
3
4
5
// 序列化:Status.ACTIVE -> "active"
objectMapper.writeValueAsString(Status.ACTIVE); // "active"

// 反序列化:"active" -> Status.ACTIVE
objectMapper.readValue("\"active\"", Status.class); // ACTIVE

MyBatis 枚举处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum Status implements BaseEnum<Integer> {
ACTIVE(1, "激活"),
INACTIVE(0, "未激活");

private final Integer code;
private final String label;

Status(Integer code, String label) {
this.code = code;
this.label = label;
}

@Override
public Integer getCode() {
return code;
}

@Override
public String getLabel() {
return label;
}
}
1
2
3
4
# MyBatis 配置
mybatis:
type-handlers:
- package: com.example.handler
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
@MappedJdbcTypes(JdbcType.INTEGER)
public class StatusTypeHandler extends BaseTypeHandler<Status> {

@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Status parameter, JdbcType jdbcType)
throws SQLException {
ps.setInt(i, parameter.getCode());
}

@Override
public Status getNullableResult(ResultSet rs, String columnName)
throws SQLException {
int code = rs.getInt(columnName);
return Status.fromCode(code);
}

@Override
public Status getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
int code = rs.getInt(columnIndex);
return Status.fromCode(code);
}

@Override
public Status getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
int code = cs.getInt(columnIndex);
return Status.fromCode(code);
}
}

实战:状态机与业务规则引擎

枚举结合状态机模式,实现复杂业务流程控制。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public enum OrderState {
// 状态定义:(当前状态, 事件, 下一个状态, 动作)
;

private final OrderStatus currentStatus;
private final OrderEvent event;
private final OrderStatus nextStatus;
private final Consumer<Order> action;

OrderState(OrderStatus currentStatus, OrderEvent event,
OrderStatus nextStatus, Consumer<Order> action) {
this.currentStatus = currentStatus;
this.event = event;
this.nextStatus = nextStatus;
this.action = action;
}

// 状态转换表
private static final Map<Transition, OrderState> TRANSITIONS = new HashMap<>();

static {
TRANSITIONS.put(
transition(DRAFT, PAY),
new OrderState(DRAFT, PAY, PAID, Order::doPay)
);
TRANSITIONS.put(
transition(PAID, SHIP),
new OrderState(PAID, SHIP, SHIPPED, Order::doShip)
);
TRANSITIONS.put(
transition(SHIPPED, RECEIVE),
new OrderState(SHIPPED, RECEIVE, COMPLETED, Order::doComplete)
);
// ... 更多转换
}

// 执行转换
public static OrderState transition(OrderStatus current,
OrderEvent event,
Order order) {
Transition key = transition(current, event);
OrderState state = TRANSITIONS.get(key);

if (state == null) {
throw new IllegalStateException(
String.format("无效转换: %s + %s -> ?", current, event)
);
}

// 执行动作
if (state.action != null) {
state.action.accept(order);
}

return state;
}

private static Transition transition(OrderStatus s, OrderEvent e) {
return new Transition(s, e);
}

// 转换键
private static class Transition {
final OrderStatus status;
final OrderEvent event;

Transition(OrderStatus status, OrderEvent event) {
this.status = status;
this.event = event;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Transition that = (Transition) o;
return status == that.status && event == that.event;
}

@Override
public int hashCode() {
return Objects.hash(status, event);
}
}
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OrderService {

public void pay(Long orderId) {
Order order = getOrder(orderId);

// 状态转换
OrderState newState = OrderState.transition(
order.getStatus(),
OrderEvent.PAY,
order
);

// 更新状态
order.setStatus(newState.nextStatus);
orderMapper.update(order);
}
}

总结

Java 枚举的进阶用法:

用法 场景
实现接口 统一管理多种实现(如支付方式)
策略模式 替代 if-else,消除大量分支
别名/扩展信息 添加 code、label 等元数据
序列化支持 Jackson、MyBatis 集成
状态机 复杂业务流程控制

枚举的优势:

  1. 类型安全:编译期检查,避免无效值
  2. 内置单例:每个枚举值是单例
  3. 可switch:直接用于 switch 语句
  4. 可扩展:可以添加方法实现复杂逻辑

合理使用枚举,能让代码更清晰、更安全。