【Enum】枚举在 Java 中的常用用法

柔情只为你懂 2023-10-03 12:08 68阅读 0赞

枚举系列文章目录:
【Enum】详解 Java 中的枚举类(上)
【Enum】详解 Java 中的枚举类(下)
【Enum】枚举在 Java 中的常用用法

相信大家通过前面几篇关于“枚举”的博文的学习,大大地提升了对枚举的认识。仅仅地知道枚举的原理不行啊,还得知道怎么去使用它。那么,下面就简单地介绍下枚举的常用用法吧~~

用法一:常量(JDK 1.5)

场景:

页面显示订单列表,且每个订单都有它自己的状态。其订单状态包括:1:未付款;2:已完成;3:待评价。现在需要根据订单状态的数值显示对应的状态(中文)。因为数据库里面存储的值是数值型的。所以,在页面需要转换为对应的中文。

之前,我们一般这样做:在常量类/接口里面定义对应的常量:

  1. public class OrderConstant {
  2. // 未付款
  3. public static final Integer NO_PAID = 1;
  4. // 已完成
  5. public static final Integer HAS_FINISHED = 2;
  6. // 待评价
  7. public static final Integer TO_EVALUATE = 3;
  8. public static final String MSG_NO_PAID = "未付款";
  9. public static final String MSG_HAS_FINISHED = "已完成";
  10. public static final String MSG_TO_EVALUATE = "待评价";
  11. }

然后,在业务逻辑层里面查询订单列表:

  1. public class OrderService {
  2. private List<TabOrder> orderList = new ArrayList<>();
  3. public void init() {
  4. orderList.add(new TabOrder("10001", "u1", OrderConstant.NO_PAID));
  5. orderList.add(new TabOrder("10002", "u1", OrderConstant.HAS_FINISHED));
  6. orderList.add(new TabOrder("10003", "u2", OrderConstant.NO_PAID));
  7. orderList.add(new TabOrder("10004", "u3", OrderConstant.TO_EVALUATE));
  8. orderList.add(new TabOrder("10005", "u3", OrderConstant.HAS_FINISHED));
  9. }
  10. // 获取所有订单
  11. public List<OrderVo> listOrders() {
  12. // 初始化订单
  13. init();
  14. List<TabOrder> orderList = getOrderList();
  15. List<OrderVo> orderVos = new ArrayList<>();
  16. if (null == orderList || orderList.size() < 0) {
  17. return orderVos;
  18. }
  19. for (TabOrder tabOrder : orderList) {
  20. OrderVo orderVo = new OrderVo();
  21. orderVo.setsId(tabOrder.getsId());
  22. orderVo.setUserId(tabOrder.getUserId());
  23. if (tabOrder.getiStatus().equals(OrderConstant.NO_PAID)) {
  24. orderVo.setStatus(OrderConstant.MSG_NO_PAID);
  25. } else if (tabOrder.getiStatus().equals(OrderConstant.HAS_FINISHED)) {
  26. orderVo.setStatus(OrderConstant.MSG_HAS_FINISHED);
  27. } else if (tabOrder.getiStatus().equals(OrderConstant.TO_EVALUATE)) {
  28. orderVo.setStatus(OrderConstant.MSG_TO_EVALUATE);
  29. }
  30. orderVos.add(orderVo);
  31. }
  32. return orderVos;
  33. }
  34. public List<TabOrder> getOrderList() {
  35. return orderList;
  36. }
  37. }

上述代码:查询出所有订单列表,然后存储在 TabOrder 类集合中,然后转换为 OrderVo 类集合,返回给前端。

获取到订单状态后,会进行状态转化:

  1. for (TabOrder tabOrder : orderList) {
  2. OrderVo orderVo = new OrderVo();
  3. orderVo.setsId(tabOrder.getsId());
  4. orderVo.setUserId(tabOrder.getUserId());
  5. // 订单状态转换
  6. if (tabOrder.getiStatus().equals(OrderConstant.NO_PAID)) {
  7. orderVo.setStatus(OrderConstant.MSG_NO_PAID);
  8. } else if (tabOrder.getiStatus().equals(OrderConstant.HAS_FINISHED)) {
  9. orderVo.setStatus(OrderConstant.MSG_HAS_FINISHED);
  10. } else if (tabOrder.getiStatus().equals(OrderConstant.TO_EVALUATE)) {
  11. orderVo.setStatus(OrderConstant.MSG_TO_EVALUATE);
  12. }
  13. orderVos.add(orderVo);
  14. }

对应数据库的字段的实体类:

  1. public class TabOrder {
  2. // 主键
  3. private String sId;
  4. // 用户id
  5. private String userId;
  6. // 订单状态
  7. private Integer iStatus;
  8. // getter/setter/toString
  9. }

返回给前端的 Vo 类:

  1. public class OrderVo {
  2. private String sId;
  3. private String userId;
  4. private String status;
  5. // getter/setter/toString
  6. }

TabOrderOrderVo的区别:TabOrder 中的订单状态使用数值型表示,而 OrderVo 类中的订单状态使用中文表示。

那么,上述使用常量的缺点:代码臃肿、不易维护。常量类中既记录了订单状态数值,又记录了订单状态中文描述;在业务逻辑层中还要对订单状态进行判断,然后才能转换为对应的中文描述。如果新增了一个订单状态,那么,改动就稍微有点大了。不仅要改动常量类,还要改动业务逻辑层。

那么,使用了枚举类之后呢?

首先,增加一个订单状态枚举类:

  1. public enum OrderStatusEnum {
  2. UNKNOW(0, "未知")
  3. ,
  4. NO_PAID(1, "未付款")
  5. ,
  6. HAS_FINISHED(2, "已完成")
  7. ,
  8. TO_EVALUATE(3, "待评价")
  9. ;
  10. private Integer status;
  11. private String desc;
  12. OrderStatusEnum(Integer status, String desc) {
  13. this.status = status;
  14. this.desc = desc;
  15. }
  16. // 根据订单状态获取订单枚举类型
  17. public static OrderStatusEnum getOrderStatusEnumByStatus(Integer status) {
  18. OrderStatusEnum[] values = OrderStatusEnum.values();
  19. if (null == values || values.length < 1) {
  20. return OrderStatusEnum.UNKNOW;
  21. }
  22. for (OrderStatusEnum value : values) {
  23. if (value.getStatus().equals(status)) {
  24. return value;
  25. }
  26. }
  27. return OrderStatusEnum.UNKNOW;
  28. }
  29. // getter
  30. }

然后,修改业务逻辑层代码(只修改 for 循环语句):

  1. for (TabOrder tabOrder : orderList) {
  2. OrderVo orderVo = new OrderVo();
  3. orderVo.setsId(tabOrder.getsId());
  4. orderVo.setUserId(tabOrder.getUserId());
  5. OrderStatusEnum orderStatusEnum = OrderStatusEnum.getOrderStatusEnumByStatus(tabOrder.getiStatus());
  6. orderVo.setStatus(orderStatusEnum.getDesc());
  7. orderVos.add(orderVo);
  8. }

这样,依旧可以达到相同的效果。可见,使用枚举类型确实可以简化代码开发。

但是,并不是所有的常量都可以使用枚举进行替换!!只是针对某一类场景确实是至关重要的:常量之间存在关联关系。如:订单状态的数值型与中文描述。此时,使用枚举能大大简化我们的工作

那么,为什么不可以用枚举替换所有的常量呢?枚举用起来多方便啊

在 Java 官方文档中有提到,不建议使用枚举:

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

大致意思:使用枚举会比使用静态变量多消耗两倍的内存。

为什么枚举会占内存?

简单来说:通过反编译后,枚举类型转化为一个类,会帮我们生成两个属性(name、ordinal)、枚举实例、声明了一个枚举对象的数组。而使用常量,只会占用数据类型对应的字节 * 常量个数。这样一对比,枚举占用内存的大小比静态变量多得多。Java 枚举(enum) 详解7种常见的用法 原理 枚举占用内存的原因。

所以,最后到底用不用枚举?在实际开发中,还需要根据实际使用场景去斟酌,杜绝滥用。

用法二:switch 语句(JDK 1.6)

JDK1.6 之前的 switch 语句只支持 int、char、
JDK1.6,switch 语句支持 enum 类型。使用枚举,能让我们的代码可读性更强。

  1. public enum SeasonEnum {
  2. SPRING,
  3. SUMMER,
  4. AUTUMN,
  5. WINTER;
  6. }
  7. public class TestEnum {
  8. // 判断是否是四季
  9. public void judgeSeason(SeasonEnum seasonEnum) {
  10. switch (seasonEnum) {
  11. case SPRING:
  12. System.out.println("这是春天");
  13. break;
  14. case SUMMER:
  15. System.out.println("这是夏天");
  16. break;
  17. case AUTUMN:
  18. System.out.println("这是秋天");
  19. break;
  20. case WINTER:
  21. System.out.println("这是冬天");
  22. break;
  23. default:
  24. System.out.println("这是错误的季节");
  25. }
  26. }
  27. public static void main(String[] args) {
  28. TestEnum testEnum = new TestEnum();
  29. SeasonEnum spring = SeasonEnum.SPRING;
  30. testEnum.judgeSeason(spring);
  31. }
  32. }

用法三:抽象方法

定义一个含有抽象方法的枚举类:

  1. public enum OrderEnum {
  2. // 未付款
  3. NO_PAID(1) {
  4. @Override
  5. public String getOrderStatus() {
  6. return "未付款";
  7. }
  8. }
  9. ,
  10. // 已完成
  11. HAS_FINISHED(2) {
  12. @Override
  13. public String getOrderStatus() {
  14. return "已完成";
  15. }
  16. }
  17. ,
  18. // 待评价
  19. TO_EVALUATE(3) {
  20. @Override
  21. public String getOrderStatus() {
  22. return "待评价";
  23. }
  24. };
  25. private Integer code;
  26. OrderEnum(Integer code) {
  27. this.code = code;
  28. }
  29. public abstract String getOrderStatus();
  30. }

测试:

  1. public class Test {
  2. public static void main(String[] args) {
  3. OrderEnum orderEnum = OrderEnum.NO_PAID;
  4. String orderStatus = orderEnum.getOrderStatus();
  5. System.out.println(orderStatus);
  6. orderEnum = OrderEnum.HAS_FINISHED;
  7. orderStatus = orderEnum.getOrderStatus();
  8. System.out.println(orderStatus);
  9. }
  10. }

用法四:实现接口

定义一个公共的接口,用来返回枚举中的属性的值

  1. public interface IErrorCode<K, V, T extends Enum> {
  2. // 返回一个枚举
  3. T get();
  4. // 返回状态码
  5. K getCode();
  6. // 返回信息
  7. V getMsg();
  8. }

定义一个状态码的枚举类

  1. public enum CodeStatusEnum implements IErrorCode<Integer, String, CodeStatusEnum> {
  2. EMPTY_PARAM(1001, "参数为空")
  3. ,
  4. ERROR_PARAM(1002, "参数错误")
  5. ,
  6. EMPTY_OBJECT(1003, "对象为空")
  7. ;
  8. private Integer code;
  9. private String msg;
  10. CodeStatusEnum(Integer code, String msg) {
  11. this.code = code;
  12. this.msg = msg;
  13. }
  14. @Override
  15. public CodeStatusEnum get() {
  16. return this;
  17. }
  18. @Override
  19. public Integer getCode() {
  20. return code;
  21. }
  22. @Override
  23. public String getMsg() {
  24. return msg;
  25. }
  26. }

定义一个订单状态的枚举类

  1. public enum OrderStatusEnum implements IErrorCode<Integer, String, CodeStatusEnum> {
  2. UNKNOW(0, "未知")
  3. ,
  4. NO_PAID(1, "未付款")
  5. ,
  6. HAS_FINISHED(2, "已完成")
  7. ,
  8. TO_EVALUATE(3, "待评价")
  9. ;
  10. private Integer status;
  11. private String desc;
  12. OrderStatusEnum(Integer code, String msg) {
  13. this.code = code;
  14. this.msg = msg;
  15. }
  16. @Override
  17. public CodeStatusEnum get() {
  18. return this;
  19. }
  20. @Override
  21. public Integer getCode() {
  22. return code;
  23. }
  24. @Override
  25. public String getMsg() {
  26. return msg;
  27. }
  28. }

测试:

  1. public class Test {
  2. public static void test(IErrorCode iErrorCode) {
  3. if (iErrorCode.get() == CodeStatusEnum.EMPTY_OBJECT) {
  4. System.out.println(MessageFormat.format("key: {0}, value: {1}", iErrorCode.getCode(), iErrorCode.getMsg()));
  5. }
  6. }
  7. public static void main(String[] args) {
  8. IErrorCode<Integer, String, CodeStatusEnum> iErrorCode = CodeStatusEnum.EMPTY_OBJECT;
  9. test(iErrorCode);
  10. }
  11. }

总结:项目中使用同一接口管理枚举类, 在方法参数中使用接口而不是用具体的枚举对象作为入参, 可以一定程度上降低程序的耦合性

另外一种用法:提取一个公共的方法,通过code获取其msg

  1. public class EnumUtil {
  2. public static <T extends ICode> T getByCode(Integer code, Class<T> enumClass) {
  3. if (code != null) {
  4. for (T each : enumClass.getEnumConstants()) {
  5. if (code.equals(each.getCode())) {
  6. return each;
  7. }
  8. }
  9. }
  10. throw new RuntimeException("没有可匹配的code:" + code);
  11. }
  12. }

用法五:接口组织枚举

  1. interface Food {
  2. enum Appetizer implements Food {
  3. SALAD, SOUP, SPRING_ROLLS;
  4. }
  5. enum MainCourse implements Food {
  6. LASAGNE, BURRITO, PAD_THAI,
  7. LENTILS, HUMMOUS, VINDALOO;
  8. }
  9. enum Dessert implements Food {
  10. TIRAMISU, GELATO, BLACK_FOREST_CAKE,
  11. FRUIT, CREME_CARAMEL;
  12. }
  13. }
  14. public class InterfaceOrganizeEnum {
  15. public static void main(String[] args) {
  16. Food food = Appetizer.SALAD;
  17. food = MainCourse.LASAGNE;
  18. }
  19. }

暂不知道这个用法的使用场景。

===========================================================================================
2021-12-09 更:

用法六:枚举里面定义一个抽象方法

优化前:

  1. Integer personType = 2;
  2. if (-1 == personType) {
  3. // TODO
  4. } else if (1 == personType) {
  5. // TODO
  6. } else if (2 == personType) {
  7. // TODO
  8. }

优化后:

  1. public enum PersonEnum {
  2. ERROR(-1) {
  3. @Override
  4. public void doSomething() {
  5. System.out.println("ERROR");
  6. }
  7. }
  8. ,
  9. CHILDREN(1) {
  10. @Override
  11. public void doSomething() {
  12. System.out.println("哭哭哭");
  13. }
  14. }
  15. ,
  16. ADULT(2) {
  17. @Override
  18. public void doSomething() {
  19. System.out.println("挣挣挣");
  20. }
  21. }
  22. ,
  23. OLD(3) {
  24. @Override
  25. public void doSomething() {
  26. System.out.println("走走走");
  27. }
  28. }
  29. ;
  30. public static PersonEnum getPersonEnumByType(Integer iPersonType) {
  31. PersonEnum[] values = PersonEnum.values();
  32. if (null == values || values.length == 0) {
  33. return PersonEnum.ERROR;
  34. }
  35. for (PersonEnum personEnum : values) {
  36. if (personEnum.getiPersonType().equals(iPersonType)) {
  37. return personEnum;
  38. }
  39. }
  40. return PersonEnum.ERROR;
  41. }
  42. private Integer iPersonType;
  43. PersonEnum(Integer iPersonType) {
  44. this.iPersonType = iPersonType;
  45. }
  46. // 抽象方法
  47. public abstract void doSomething();
  48. public Integer getiPersonType() {
  49. return iPersonType;
  50. }
  51. }

测试:

  1. public class PersonEnumTest {
  2. public static void main(String[] args) {
  3. Integer personType = -1;
  4. PersonEnum.getPersonEnumByType(personType).doSomething();
  5. }
  6. }

发表评论

表情:
评论列表 (有 0 条评论,68人围观)

还没有评论,来说两句吧...

相关阅读

    相关 enum

    JDK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。 大师兄我又加上自己的理解,来帮助各位理解一下。 用法一:常量