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

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

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

用法一:常量(JDK 1.5)

场景:

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

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

public class OrderConstant {
    // 未付款
    public static final Integer NO_PAID = 1;
    // 已完成
    public static final Integer HAS_FINISHED = 2;
    // 待评价
    public static final Integer TO_EVALUATE = 3;

    public static final String MSG_NO_PAID = "未付款";
    public static final String MSG_HAS_FINISHED = "已完成";
    public static final String MSG_TO_EVALUATE = "待评价";
}

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

public class OrderService {

    private List<TabOrder> orderList = new ArrayList<>();

    public void init() {
        orderList.add(new TabOrder("10001", "u1", OrderConstant.NO_PAID));
        orderList.add(new TabOrder("10002", "u1", OrderConstant.HAS_FINISHED));
        orderList.add(new TabOrder("10003", "u2", OrderConstant.NO_PAID));
        orderList.add(new TabOrder("10004", "u3", OrderConstant.TO_EVALUATE));
        orderList.add(new TabOrder("10005", "u3", OrderConstant.HAS_FINISHED));
    }

    // 获取所有订单
    public List<OrderVo> listOrders() {
        // 初始化订单
        init();

        List<TabOrder> orderList = getOrderList();
        List<OrderVo> orderVos = new ArrayList<>();

        if (null == orderList || orderList.size() < 0) {
            return orderVos;
        }

        for (TabOrder tabOrder : orderList) {
            OrderVo orderVo = new OrderVo();

            orderVo.setsId(tabOrder.getsId());
            orderVo.setUserId(tabOrder.getUserId());
            if (tabOrder.getiStatus().equals(OrderConstant.NO_PAID)) {
                orderVo.setStatus(OrderConstant.MSG_NO_PAID);
            } else if (tabOrder.getiStatus().equals(OrderConstant.HAS_FINISHED)) {
                orderVo.setStatus(OrderConstant.MSG_HAS_FINISHED);
            } else if (tabOrder.getiStatus().equals(OrderConstant.TO_EVALUATE)) {
                orderVo.setStatus(OrderConstant.MSG_TO_EVALUATE);
            }
            orderVos.add(orderVo);
        }
        return orderVos;
    }

    public List<TabOrder> getOrderList() {
        return orderList;
    }
}

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

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

for (TabOrder tabOrder : orderList) {
    OrderVo orderVo = new OrderVo();

    orderVo.setsId(tabOrder.getsId());
    orderVo.setUserId(tabOrder.getUserId());
    
    // 订单状态转换
    if (tabOrder.getiStatus().equals(OrderConstant.NO_PAID)) {
        orderVo.setStatus(OrderConstant.MSG_NO_PAID);
    } else if (tabOrder.getiStatus().equals(OrderConstant.HAS_FINISHED)) {
        orderVo.setStatus(OrderConstant.MSG_HAS_FINISHED);
    } else if (tabOrder.getiStatus().equals(OrderConstant.TO_EVALUATE)) {
        orderVo.setStatus(OrderConstant.MSG_TO_EVALUATE);
    }
    orderVos.add(orderVo);
}

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

public class TabOrder {
	// 主键
    private String sId;
    // 用户id
    private String userId;
    // 订单状态
    private Integer iStatus;
	
	// getter/setter/toString
}

返回给前端的 Vo 类:

public class OrderVo {
    private String sId;
    private String userId;
    private String status;

	// getter/setter/toString
}

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

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

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

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

public enum OrderStatusEnum {
    UNKNOW(0, "未知")
    ,
    NO_PAID(1, "未付款")
    ,
    HAS_FINISHED(2, "已完成")
    ,
    TO_EVALUATE(3, "待评价")
    ;

    private Integer status;
    private String desc;

    OrderStatusEnum(Integer status, String desc) {
        this.status = status;
        this.desc = desc;
    }

    // 根据订单状态获取订单枚举类型
    public static OrderStatusEnum getOrderStatusEnumByStatus(Integer status) {
        OrderStatusEnum[] values = OrderStatusEnum.values();
        if (null == values || values.length < 1) {
            return OrderStatusEnum.UNKNOW;
        }

        for (OrderStatusEnum value : values) {
            if (value.getStatus().equals(status)) {
                return value;
            }
        }
        return OrderStatusEnum.UNKNOW;
    }

    // getter
}

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

for (TabOrder tabOrder : orderList) {
    OrderVo orderVo = new OrderVo();

    orderVo.setsId(tabOrder.getsId());
    orderVo.setUserId(tabOrder.getUserId());

    OrderStatusEnum orderStatusEnum = OrderStatusEnum.getOrderStatusEnumByStatus(tabOrder.getiStatus());
    orderVo.setStatus(orderStatusEnum.getDesc());
    
    orderVos.add(orderVo);
}

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

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

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

在 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 类型。使用枚举,能让我们的代码可读性更强。

public enum SeasonEnum {
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER;
}
public class TestEnum {
	
	// 判断是否是四季
    public void judgeSeason(SeasonEnum seasonEnum) {
        switch (seasonEnum) {
            case SPRING:
                System.out.println("这是春天");
                break;
            case SUMMER:
                System.out.println("这是夏天");
                break;
            case AUTUMN:
                System.out.println("这是秋天");
                break;
            case WINTER:
                System.out.println("这是冬天");
                break;
            default:
                System.out.println("这是错误的季节");
        }
    }

    public static void main(String[] args) {
        TestEnum testEnum = new TestEnum();
        SeasonEnum spring = SeasonEnum.SPRING;

        testEnum.judgeSeason(spring);
    }
}

用法三:抽象方法

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

public enum OrderEnum {

    // 未付款
    NO_PAID(1) {
        @Override
        public String getOrderStatus() {
            return "未付款";
        }
    }
    ,
    // 已完成
    HAS_FINISHED(2) {
        @Override
        public String getOrderStatus() {
            return "已完成";
        }
    }
    ,
    // 待评价
    TO_EVALUATE(3) {
        @Override
        public String getOrderStatus() {
            return "待评价";
        }
    };

    private Integer code;

    OrderEnum(Integer code) {
        this.code = code;
    }

    public abstract String getOrderStatus();
}

测试:

public class Test {

    public static void main(String[] args) {
        OrderEnum orderEnum = OrderEnum.NO_PAID;
        String orderStatus = orderEnum.getOrderStatus();
        System.out.println(orderStatus);

        orderEnum = OrderEnum.HAS_FINISHED;
        orderStatus = orderEnum.getOrderStatus();
        System.out.println(orderStatus);
    }
}

用法四:实现接口

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

public interface IErrorCode<K, V, T extends Enum> {
    // 返回一个枚举
    T get();
    // 返回状态码
    K getCode();
    // 返回信息
    V getMsg();
}

定义一个状态码的枚举类

public enum CodeStatusEnum implements IErrorCode<Integer, String, CodeStatusEnum> {

    EMPTY_PARAM(1001, "参数为空")
    ,
    ERROR_PARAM(1002, "参数错误")
    ,
    EMPTY_OBJECT(1003, "对象为空")
    ;

    private Integer code;
    private String msg;

    CodeStatusEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
	@Override
    public CodeStatusEnum get() {
        return this;
    }
    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}

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

public enum OrderStatusEnum implements IErrorCode<Integer, String, CodeStatusEnum> {
    UNKNOW(0, "未知")
    ,
    NO_PAID(1, "未付款")
    ,
    HAS_FINISHED(2, "已完成")
    ,
    TO_EVALUATE(3, "待评价")
    ;

    private Integer status;
    private String desc;

    OrderStatusEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
    @Override
    public CodeStatusEnum get() {
        return this;
    }
    
    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}

测试:

public class Test {

    public static void test(IErrorCode iErrorCode) {
        if (iErrorCode.get() == CodeStatusEnum.EMPTY_OBJECT) {
            System.out.println(MessageFormat.format("key: {0}, value: {1}", iErrorCode.getCode(), iErrorCode.getMsg()));
        }
    }

    public static void main(String[] args) {
        IErrorCode<Integer, String, CodeStatusEnum> iErrorCode = CodeStatusEnum.EMPTY_OBJECT;
        test(iErrorCode);
    }
}

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

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

public class EnumUtil {

    public static <T extends ICode> T getByCode(Integer code, Class<T> enumClass) {
        if (code != null) {
            for (T each : enumClass.getEnumConstants()) {
                if (code.equals(each.getCode())) {
                    return each;
                }
            }
        }
        throw new RuntimeException("没有可匹配的code:" + code);
    }
}

用法五:接口组织枚举

interface Food {
    
    enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
    }
    
    enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
    }
    
    enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
    }
}
 
public class InterfaceOrganizeEnum {
    public static void main(String[] args) {
        Food food = Appetizer.SALAD;
        food = MainCourse.LASAGNE;
    }
}

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

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

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

优化前:

Integer personType = 2;
if (-1 == personType) {
	// TODO
} else if (1 == personType) {
	// TODO
} else if (2 == personType) {
	// TODO
}

优化后:

public enum PersonEnum {

    ERROR(-1) {
        @Override
        public void doSomething() {
            System.out.println("ERROR");
        }
    }
    ,
    CHILDREN(1) {
        @Override
        public void doSomething() {
            System.out.println("哭哭哭");
        }
    }
    ,
    ADULT(2) {
        @Override
        public void doSomething() {
            System.out.println("挣挣挣");
        }
    }
    ,
    OLD(3) {
        @Override
        public void doSomething() {
            System.out.println("走走走");
        }
    }
    ;

    public static PersonEnum getPersonEnumByType(Integer iPersonType) {
        PersonEnum[] values = PersonEnum.values();
        if (null == values || values.length == 0) {
            return PersonEnum.ERROR;
        }
        for (PersonEnum personEnum : values) {
            if (personEnum.getiPersonType().equals(iPersonType)) {
                return personEnum;
            }
        }
        return PersonEnum.ERROR;
    }

    private Integer iPersonType;

    PersonEnum(Integer iPersonType) {
        this.iPersonType = iPersonType;
    }
	
	// 抽象方法
    public abstract void doSomething();

    public Integer getiPersonType() {
        return iPersonType;
    }
}

测试:

public class PersonEnumTest {

    public static void main(String[] args) {
        Integer personType = -1;
        PersonEnum.getPersonEnumByType(personType).doSomething();
    }

}

你可能感兴趣的:(Java,java,开发语言,后端)