Java枚举类型进阶实战:状态定义、策略模式与统一返回码

前言

枚举(’enum‘)是Java 5引入的关键字,但很多人只把它当作“高级常量”使用。实际上,枚举可以拥有属性、方法、构造函数,甚至实现接口,在状态机、策略,模式和接口规范等场景中能大幅提升代码的有雅兴和安全性。
本文将结合三个经典业务场景,带你重新认识枚举的威力:
1.状态/类型定义:告别离散常量,用枚举打造类型安全的状态字典。
2.策略模式(替换大量if/else):将行为绑定到枚举实例,消除冗长的分支判断。
3.统一返回码(后端接口必备):定义标准响应码枚举,结合统一返回对象,让接口更加规范。
文中所有示例均可直接运行,读者可跟随编码练习。


一、场景1:状态/类型定义

1.1痛苦的常量方式

很多系统中会定义一堆状态常量:

public class OrderStatus {
    public static final int PENDING   = 0;
    public static final int PROCESSING = 1;
    public static final int SHIPPED   = 2;
    public static final int DELIVERED = 3;
    public static final int CANCELLED = 4;
}

缺点:
类型不安全:方法接收int status,可以传入任意整型。
无命名空间:打印时只是数字,难以阅读。
无法携带额外信息(如状态描述)。

1.2枚举改造

public enum OrderStatus {
    PENDING(0, "待处理"),
    PROCESSING(1, "处理中"),
    SHIPPED(2, "已发货"),
    DELIVERED(3, "已送达"),
    CANCELLED(4, "已取消");

    private final int code;
    private final String desc;

    OrderStatus(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() { return code; }
    public String getDesc() { return desc; }

    // 根据 code 转换,常用于接收参数
    public static OrderStatus fromCode(int code) {
        for (OrderStatus status : values()) {
            if (status.code == code) return status;
        }
        throw new IllegalArgumentException("未知状态码: " + code);
    }
}

1.3使用示例

public void updateOrderStatus(Long orderId, OrderStatus status) {
    // 类型安全,只能传入枚举定义中的值
    System.out.println("订单状态更新为:" + status.getDesc());
}

// 调用
updateOrderStatus(123L, OrderStatus.SHIPPED);

优势总结:
类型安全,编译器检查。
语义化强,自带描述信息。
易于扩展(增加状态只需要添加枚举示例)。

二、场景2:策略模式(替换大量if/else)

2.1传统if/else的痛点

假设我们要做一个计算器,根据操作符执行不同运算:

public int calculate(int a, int b, String operator) {
    if ("+".equals(operator)) {
        return a + b;
    } else if ("-".equals(operator)) {
        return a - b;
    } else if ("*".equals(operator)) {
        return a * b;
    } else if ("/".equals(operator)) {
        return a / b;
    } else {
        throw new IllegalArgumentException("不支持的操作符");
    }
}

随着操作增多,分支判断会越来越臃肿,违反开闭原则。

2.2枚举实现策略模式

我们可以定义一个操作枚举,并在每个枚举实例中实现具体的运算逻辑。

public enum Operator {
    ADD {
        @Override
        public int apply(int a, int b) { return a + b; }
    },
    SUBTRACT {
        @Override
        public int apply(int a, int b) { return a - b; }
    },
    MULTIPLY {
        @Override
        public int apply(int a, int b) { return a * b; }
    },
    DIVIDE {
        @Override
        public int apply(int a, int b) {
            if (b == 0) throw new ArithmeticException("除数不能为0");
            return a / b;
        }
    };

    public abstract int apply(int a, int b);
}

也可以定义接口,让枚举实现该接口,效果类似。

2.3 调用方式

public int calculate(int a, int b, Operator op) {
    return op.apply(a, b);
}

// 使用
int result = calculate(10, 5, Operator.ADD);
System.out.println(result); // 15

扩展新操作时,只需增加一个枚举示例,无需修改原有代码,完美契合开闭原则。

2.4更复杂策略:支付方式举例

public enum PaymentMethod {
    ALIPAY {
        @Override
        public void pay(BigDecimal amount) {
            System.out.println("使用支付宝支付:¥" + amount);
            // 调用真实支付接口
        }
    },
    WECHAT {
        @Override
        public void pay(BigDecimal amount) {
            System.out.println("使用微信支付:¥" + amount);
        }
    },
    CREDIT_CARD {
        @Override
        public void pay(BigDecimal amount) {
            System.out.println("使用信用卡支付:¥" + amount);
        }
    };

    public abstract void pay(BigDecimal amount);
}

调用时只需PaymentMethod.ALIPAY.pay(new BigDecimal(“99.9”)),完全摆脱if/else。
优势总结:
消除冗长的条件分支。
新增策略只需添加枚举实例,符合开闭原则。
枚举天然单例,每个策略实例全局唯一,线程安全。

三、场景3:统一返回码(后端接口必备)

3.1为什么需要统一返回码

后端接口一般会返回如下JSON:

{
  "code": 200,
  "message": "操作成功",
  "data": { ... }
}

如果每次都要动手写code和message,不仅代码重复,而且状态码容易散落在各处,难以维护。

3.2定义响应码枚举

public enum ResultCode {
    SUCCESS(200, "操作成功"),
    BAD_REQUEST(400, "参数错误"),
    UNAUTHORIZED(401, "未授权"),
    FORBIDDEN(403, "无权限"),
    NOT_FOUND(404, "资源不存在"),
    SERVER_ERROR(500, "服务器内部错误");

    private final int code;
    private final String message;

    ResultCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() { return code; }
    public String getMessage() { return message; }
}

3.3统一响应体

public class ApiResult<T> {
    private int code;
    private String message;
    private T data;

    // 私有构造,通过静态方法创建
    private ApiResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 成功响应
    public static <T> ApiResult<T> success(T data) {
        return new ApiResult<>(ResultCode.SUCCESS.getCode(),
                               ResultCode.SUCCESS.getMessage(),
                               data);
    }

    // 失败响应,使用指定错误码
    public static <T> ApiResult<T> error(ResultCode resultCode) {
        return new ApiResult<>(resultCode.getCode(),
                               resultCode.getMessage(),
                               null);
    }

    // getter 省略...
}

3.4在Controller中使用

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/{id}")
    public ApiResult<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user == null) {
            return ApiResult.error(ResultCode.NOT_FOUND);
        }
        return ApiResult.success(user);
    }
}

返回结果示例:

{
  "code": 200,
  "message": "操作成功",
  "data": { "id": 1, "name": "张三" }
}

优势总结:

接口返回格式统一,便于前端统一拦截处理。

错误码和描述集中在枚举中,便于查阅和维护。

减少硬编码,提升代码可读性。

四、总结

本文通过三个典型场景展示了枚举从“常量容器”升级为“逻辑封装器”的过程:

状态/类型定义:让枚举携带元数据,告别魔法数字。

策略模式:将行为内聚到枚举实例,消除 if/else,拥抱开闭原则。

统一返回码:结合响应体构建优雅的后端接口规范。

枚举在 Java 中远不止是 class 的附属,它是构建类型安全、高内聚、易扩展代码的利器。希望本文能给你带来启发,在项目中大胆使用枚举来简化设计。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐