枚举实战

Java 枚举的 3 个实战场景:从状态码到策略模式

枚举不只是"定义常量",用好了能让代码干净一半。

适合人群 :刚学完 Enum 语法、想知道"实际项目中怎么用"的同学。

阅读时间:约 8 分钟。


目录

  • [场景 1:状态 / 类型定义(最常用)](#场景 1:状态 / 类型定义(最常用))
  • [场景 2:策略模式(替换大量 if/else)](#场景 2:策略模式(替换大量 if/else))
  • [场景 3:统一返回码(后端接口必备)](#场景 3:统一返回码(后端接口必备))
  • 完整可运行代码
  • 总结

场景 1:状态 / 类型定义(最常用)

问题:魔法数字与魔法字符串

java 复制代码
// 烂代码示例
if (order.getStatus() == 1) {
    // 待支付
} else if (order.getStatus() == 2) {
    // 已支付
}
// 1 和 2 是什么意思?新同事一脸懵,数据库里也是裸数字

枚举重构

文件:OrderStatus.java

java 复制代码
public enum OrderStatus {
    PENDING_PAYMENT("待支付", 1),
    PAID("已支付", 2),
    SHIPPED("已发货", 3),
    COMPLETED("已完成", 4),
    CANCELLED("已取消", 5);

    private final String desc;
    private final int code;

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

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

    // 根据 code 反查枚举(数据库里存的是 code)
    public static OrderStatus of(int code) {
        for (OrderStatus status : values()) {
            if (status.code == code) return status;
        }
        throw new IllegalArgumentException("未知状态码: " + code);
    }
}

文件:OrderStatusDemo.java

java 复制代码
public class OrderStatusDemo {
    public static void main(String[] args) {
        // 1. 基本使用
        OrderStatus status = OrderStatus.PAID;
        System.out.println("当前状态: " + status.getDesc());
        System.out.println("状态码: " + status.getCode());

        // 2. 遍历所有状态(适合展示枚举的规范性)
        System.out.println("\n=== 所有订单状态 ===");
        for (OrderStatus s : OrderStatus.values()) {
            System.out.printf("code=%d, desc=%s%n", s.getCode(), s.getDesc());
        }

        // 3. 根据 code 反查(数据库场景)
        OrderStatus fromDb = OrderStatus.of(3);
        System.out.println("\n从数据库 code=3 反查: " + fromDb);
    }
}

运行截图

对比

维度 魔法数字 枚举
可读性 需要查文档或注释 自描述
类型安全 任意 int 都能传 编译期检查
维护性 改一个值要全局搜索 改枚举定义一处即可
数据库映射 手动维护 通过 getCode() / of() 统一映射

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

问题:臃肿的分支判断

java 复制代码
public BigDecimal calculatePrice(String role, BigDecimal amount) {
    if ("VIP".equals(role)) {
        return amount.multiply(new BigDecimal("0.9"));
    } else if ("SVIP".equals(role)) {
        return amount.multiply(new BigDecimal("0.75"));
    } else if ("NORMAL".equals(role)) {
        return amount;
    }
    // 每新增一个角色,就要改这里,违反开闭原则
    throw new IllegalArgumentException("未知角色: " + role);
}

痛点:新增角色要改核心方法,容易出错,且逻辑和配置混在一起。

枚举策略重构

文件:DiscountStrategy.java

java 复制代码
import java.math.BigDecimal;

public enum DiscountStrategy {
    NORMAL("普通会员") {
        @Override
        public BigDecimal apply(BigDecimal amount) {
            return amount; // 无折扣
        }
    },
    VIP("VIP会员") {
        @Override
        public BigDecimal apply(BigDecimal amount) {
            return amount.multiply(new BigDecimal("0.9")); // 9折
        }
    },
    SVIP("SVIP会员") {
        @Override
        public BigDecimal apply(BigDecimal amount) {
            return amount.multiply(new BigDecimal("0.75")); // 75折
        }
    };

    private final String roleName;

    DiscountStrategy(String roleName) {
        this.roleName = roleName;
    }

    public abstract BigDecimal apply(BigDecimal amount);

    public String getRoleName() {
        return roleName;
    }
}

文件:StrategyDemo.java

java 复制代码
import java.math.BigDecimal;

public class StrategyDemo {
    public static void main(String[] args) {
        BigDecimal amount = new BigDecimal("1000");

        System.out.println("=== 不同角色折扣计算 ===");
        for (DiscountStrategy strategy : DiscountStrategy.values()) {
            BigDecimal finalPrice = strategy.apply(amount);
            System.out.printf("%s: 原价 %s → 实付 %s%n", 
                strategy.getRoleName(), amount, finalPrice);
        }
    }
}

运行截图

对比

维度 if/else 版本 枚举策略版本
新增角色 修改核心类 新增枚举常量即可
单元测试 测一个长方法 每个策略独立测试
可读性 逻辑堆在一起 策略自描述
扩展性 违反开闭原则 符合开闭原则

如果策略逻辑很复杂,也可以在枚举里只存一个 Supplier<Strategy> 或函数接口,把具体实现类延迟加载。


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

问题:错误码散落各处

java 复制代码
// Controller A
return new Result(500, "服务器内部错误", null);

// Controller B
return new Result(500, "系统错误", null);
// 同样的 500,消息不一样,前端无法统一处理

枚举统一封装

文件:ResultCode.java

java 复制代码
public enum ResultCode {
    SUCCESS(200, "操作成功"),
    BAD_REQUEST(400, "请求参数错误"),
    UNAUTHORIZED(401, "未登录或 token 过期"),
    FORBIDDEN(403, "无权限访问"),
    NOT_FOUND(404, "资源不存在"),
    INTERNAL_ERROR(500, "服务器内部错误"),
    SERVICE_BUSY(503, "服务繁忙,请稍后重试");

    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; }

    // 统一包装成 Result 对象
    public <T> Result<T> toResult() {
        return new Result<>(this.code, this.message, null);
    }

    public <T> Result<T> toResult(T data) {
        return new Result<>(this.code, this.message, data);
    }
}

文件:Result.java

java 复制代码
public class Result<T> {
    private int code;
    private String message;
    private T data;

    public Result(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 快速成功
    public static <T> Result<T> ok(T data) {
        return ResultCode.SUCCESS.toResult(data);
    }

    // 快速失败
    public static <T> Result<T> error(ResultCode resultCode) {
        return resultCode.toResult();
    }

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

文件:ResultCodeDemo.java

java 复制代码
public class ResultCodeDemo {
    public static void main(String[] args) {
        // 模拟成功响应
        Result<String> success = Result.ok("用户数据");
        System.out.println("成功响应:");
        System.out.println("  code: " + success.getCode());
        System.out.println("  message: " + success.getMessage());
        System.out.println("  data: " + success.getData());

        System.out.println();

        // 模拟失败响应
        Result<String> error = Result.error(ResultCode.NOT_FOUND);
        System.out.println("失败响应:");
        System.out.println("  code: " + error.getCode());
        System.out.println("  message: " + error.getMessage());
        System.out.println("  data: " + error.getData());
    }
}

运行截图

对比

维度 硬编码返回 枚举统一返回
语义一致性 各处写法可能不同 一处定义,全局复用
维护成本 改消息要全局搜索 改枚举一处即可
国际化 难以扩展 可在枚举里扩展 getMessage(Locale)
前端协作 错误码语义不清 前后端按枚举约定对接

完整可运行代码

把以下 7 个文件放在同一个包(或同一个文件夹)下:

文件名 类型 说明
OrderStatus.java enum 场景 1 枚举定义
OrderStatusDemo.java class 场景 1 运行入口(右键运行)
DiscountStrategy.java enum 场景 2 枚举定义
StrategyDemo.java class 场景 2 运行入口(右键运行)
ResultCode.java enum 场景 3 枚举定义
Result.java class 场景 3 泛型包装类
ResultCodeDemo.java class 场景 3 运行入口(右键运行)

注意:只有带 main 方法的 *Demo.java 才能直接运行,枚举文件本身没有 main 方法,无法直接运行。

总结

三个场景速查表

场景 判断标准 核心收益
状态/类型定义 一组固定值,且需要附带描述或码值 类型安全、可读性强、数据库映射方便
策略模式 多分支逻辑,且分支可能新增 消灭 if/else,符合开闭原则
统一返回码 前后端协作,需要集中管理错误语义 错误码标准化,降低维护成本

如果对你有帮助,欢迎点赞和收藏。有任何问题或更好的实践,欢迎在评论区交流。

相关推荐
夜微凉41 小时前
三、Spring
java·后端·spring
橘右今1 小时前
2026 Java后端高频面试宝典
java·开发语言·面试
xyzzklk2 小时前
解决Salesforce无法向外发送邮件
android·java·开发语言·网络·crm·salesforce·客户关系管理
biubiubiu07063 小时前
SpringBoot关于外部化配置
java·spring boot·spring
zzz_23683 小时前
【Spring】面试突击系列(二):SpringBoot 入门与自动配置原理
java·spring boot·spring
Full Stack Developme3 小时前
Spring AOP 与 AspectJ
java·后端·spring
快乐的木子李3 小时前
最新版Maven免安装配置教程
java·maven
wuminyu4 小时前
Java锁机制之Java对象重量级锁源码剖析
java·linux·c语言·jvm·c++
艾利克斯冰5 小时前
Java设计模式-创建型设计模式
java