VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • 速食枚举

问题场景

  1. 通过枚举的编码查询枚举项
  2. 将枚举项返回给前端作为下拉框选项
  3. 将枚举项作为结果返回

问题扩展

  1. 通过枚举的编码查询枚举项 -> 检查指定枚举中是否存在某个编码(检查枚举参数的合法性)
  2. 通过枚举的编码查询枚举项 -> 如果找不到给默认值(非法参数兼容,避免null判断)
  3. 将枚举项返回给前端作为下拉框选项 -> 应支持自定义选择枚举项
  4. 将枚举项作为结果返回 -> 应提供支持JSON序列化返回值

解决思路

让枚举实现接口,将常见的使用需求抽象为静态工具方法来简化使用。

解决方案

本方案提供了三个抽象层次的接口。简要介绍三个接口的适用场景。

接口 适用场景 具备能力
CodeEnum 只在后台使用,避免魔法值
适用于不用在前端显示的场景(下拉或反显)
通过编码查询枚举项
通过编码查询枚举项,提供默认值,如果不存在返回默认值
检查编码对应枚举项是否存在
SelectEnum 扩展了CodeEnum,新增返回前端下列列表的支持
适用于基于约定开发的场景,在没有附加属性的情况下使用起来比较便捷
指定枚举,返回枚举项下拉列表
指定多个枚举项,返回枚举项下拉列表
指定枚举和编码,返回编码对应的描述
指定枚举、编码和默认值,返回编码对应的描述,如没找到枚举项,返回默认值
支持扩展下拉属性
MapEnum 扩展了SelectEnum,支持自定义参数名称配置
使用Map而非继承的方式扩展下拉属性
适用于自定义较多的场景
返回带有自定义的属性名称的下拉列表
返回指定多个枚举项下拉列表
支持扩展下拉属性

代码实现

注:代码中使用了jdk8特性

源码地址

本文代码在enums包下
https://gitee.com/ILikeSoup/instant-soup

CodeEnum.java

使用:枚举需实现getCode()方法,指定枚举项的编码

import java.util.Objects;
import java.util.Optional;

/**
 * 基于编码的枚举
 * 提供通过编码获取枚举项的能力
 *
 * @param <K> 编码类型
 * @author Soup
 */
public interface CodeEnum<K> {
    /**
     * @return 枚举项的编码,必须唯一
     */
    K getCode();

    /**
     * 通过枚举项编码获取枚举项
     *
     * @param code          枚举项编码
     * @param codeEnumClass 枚举类
     * @param <T>           枚举类型
     * @param <K>           编码类型
     * @return 对应编码的枚举项
     */
    static <T extends Enum<T> & CodeEnum<K>, K> T getByCode(K code, Class<T> codeEnumClass) {
        T[] constants = codeEnumClass.getEnumConstants();
        for (T constant : constants) {
            if (Objects.equals(constant.getCode(), code)) {
                return constant;
            }
        }
        return null;
    }

    /**
     * 通过枚举项编码获取枚举项, 如果不存在就返回默认值
     *
     * @param code          枚举项编码
     * @param codeEnumClass 枚举类
     * @param def           默认值,如果找不到匹配的项,就返回该默认值
     * @param <T>           枚举类型
     * @param <K>           编码类型
     * @return 对应编码的枚举项,如果找不到匹配的项,就返回该默认值
     */
    static <T extends Enum<T> & CodeEnum<K>, K> T getByCode(K code, Class<T> codeEnumClass, T def) {
        return Optional.ofNullable(getByCode(code, codeEnumClass)).orElse(def);
    }

    /**
     * 判断编码在枚举中是否存在
     *
     * @param code          枚举项编码
     * @param codeEnumClass 枚举类
     * @param <T>           枚举类型
     * @param <K>           编码类型
     * @return 存在返回true, 不存在返回false
     */
    static <T extends Enum<T> & CodeEnum<K>, K> boolean contains(K code, Class<T> codeEnumClass) {
        return getByCode(code, codeEnumClass) != null;
    }

}

SelectEnum.java

使用:枚举需实现getCode()和getDesc()方法,指定枚举项的编码和描述
可根据使用情况选择实现getSelectVo()方法。该方法返回null可在生成下拉列表时排除某些枚举项;也可以基于扩展属性需要返回SelectVo的子类。

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 涉及到前端下拉展示的枚举,可以实现该接口
 *
 * 扩展了 CodeEnum 接口,引用了 CodeEnum 中提供的工具方法
 *  {@link SelectEnum#getByCode} 通过code获取枚举项,可以用于反显
 *  {@link SelectEnum#contains}  通过code判断枚举项是否存在,可以用于接收参数的合法性校验
 *
 * 新增下拉用的描述项,提供了下拉框和反显会使用的工具方法
 *  {@link SelectEnum#list} 和 {@link SelectEnum#asList} 可以用于获取前端需要的下拉列表
 *  {@link SelectEnum#getDescByCode} 可以用于后台反显枚举描述
 *
 * 可以通过扩展 SelectVo 类并重写 {@link SelectEnum#getSelectVo} 来扩展下拉列表项的属性
 * 可以通过在枚举中重写 {@link SelectEnum#getSelectVo} 使其返回 null 来排除该项的前端展示
 *
 * @author  Soup
 * @param <K>   枚举项code的类型
 */
public interface SelectEnum<K> extends CodeEnum<K> {

    /**
     * @return  枚举项的描述,必须唯一(否则,下拉展示让人混淆)
     */
    String getDesc();

    /**
     * @return  由枚举项转换成的下拉项,可以通过扩展SelectVo类并重写这个方法来扩展下拉参数
     */
    default SelectVo<K> getSelectVo() {
        return new SelectVo<>(getCode(), getDesc());
    }


    /**
     * 将枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug,这里必须标识类型
     * Stream.<SelectEnum<K>>of
     *
     * @param selectEnumClass   枚举类
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    static <T extends Enum<T> & SelectEnum<K>, K> List<SelectVo<K>> list(Class<T> selectEnumClass) {
        T[] constants = selectEnumClass.getEnumConstants();
        return Stream.<SelectEnum<K>>of(constants).map(SelectEnum::getSelectVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将给定的枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug,这里必须标识类型
     * Stream.<SelectEnum<K>>of
     *
     * @param selectEnums   枚举项数组
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    @SafeVarargs
    static <T extends SelectEnum<K>, K> List<SelectVo<K>> asList(T... selectEnums) {
        Objects.requireNonNull(selectEnums);
        return Stream.<SelectEnum<K>>of(selectEnums).map(SelectEnum::getSelectVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 通过枚举项编码获取枚举项描述
     * @param code  枚举项编码
     * @param selectEnumClass   枚举类
     * @param <T>   枚举类型
     * @param <K>   编码类型
     * @return  对应编码的枚举项描述
     */
    static <T extends Enum<T> & SelectEnum<K>, K> String getDescByCode(K code, Class<T> selectEnumClass) {
        T constant = CodeEnum.getByCode(code, selectEnumClass);
        return constant == null ? null : constant.getDesc();
    }

    /**
     * 通过枚举项编码获取枚举项描述
     * @param code  枚举项编码
     * @param selectEnumClass   枚举类
     * @param def   找不到枚举时返回的默认值
     * @param <T>   枚举类型
     * @param <K>   编码类型
     * @return  对应编码的枚举项描述
     */
    static <T extends Enum<T> & SelectEnum<K>, K> String getDescByCode(K code, Class<T> selectEnumClass, String def) {
        T constant = CodeEnum.getByCode(code, selectEnumClass);
        return constant == null ? def : constant.getDesc();
    }

    /**
     * 通过枚举项编码获取枚举项
     * @param code  枚举项编码
     * @param selectEnumClass   枚举类
     * @param <T>   枚举类型
     * @param <K>   编码类型
     * @return  对应编码的枚举项
     */
    static <T extends Enum<T> & SelectEnum<K>, K> T getByCode(K code, Class<T> selectEnumClass) {
        return CodeEnum.getByCode(code, selectEnumClass);
    }

    /**
     * 通过枚举项编码获取枚举项
     * @param code  枚举项编码
     * @param selectEnumClass   枚举类
     * @param def   默认值,如果找不到匹配的项,就返回该默认值
     * @param <T>   枚举类型
     * @param <K>   编码类型
     * @return  对应编码的枚举项,如果找不到匹配的项,就返回该默认值
     */
    static <T extends Enum<T> & SelectEnum<K>, K> T getByCode(K code, Class<T> selectEnumClass, T def) {
        return CodeEnum.getByCode(code, selectEnumClass, def);
    }

    /**
     * 判断编码在枚举中是否存在
     * @param code  枚举项编码
     * @param selectEnumClass   枚举类
     * @param <T>   枚举类型
     * @param <K>   编码类型
     * @return  存在返回true,不存在返回false
     */
    static <T extends Enum<T> & SelectEnum<K>, K> boolean contains(K code, Class<T> selectEnumClass) {
        return getByCode(code, selectEnumClass) != null;
    }

    class SelectVo<K> {
        private K code;
        private String desc;

        public SelectVo(K code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public K getCode() {
            return code;
        }

        public String getDesc() {
            return desc;
        }

        @Override
        public String toString() {
            return "CodeDesc{" +
                    "code='" + code + '\'' +
                    ", desc='" + desc + '\'' +
                    '}';
        }
    }

}

MapEnum.java

使用:枚举需实现getCode()和getDesc()方法,指定枚举项的编码和描述
可选择实现getCodeKey()和getDescKey()方法,来修改编码和描述的属性名称。
可选择实现getInitMap()使用指定Map实现,例如:要求属性有序时选择LinkedHashMap
可选择实现extendVo()在默认提供的编码和描述基础上扩展自定义属性
可选择实现getMapVo()完全自定义属性

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 扩展了 SelectEnum
 * 使用 Map 作为返回值,以支持更灵活的扩展,摆脱 {@link SelectEnum.SelectVo} 的限制
 *
 * 支持自定义Map编码和描述的键名称。
 * 重写 {@link MapEnum#extendVo} 扩展Map内容
 *
 * 提供了静态方法返回 Map 列表,与 SelectEnum 中的静态方法类似。
 * {@link MapEnum#list}
 * {@link MapEnum#asList}
 * 两个方法均有重载,在没有重写 {@link MapEnum#getCodeKey} 和 {@link MapEnum#getDescKey} 的情况下,
 * 直接调用重载的静态方法,通过参数指定键名
 *
 * Map 默认使用 {@link java.util.HashMap} 实现
 * 可以重写 {@link MapEnum#getInitMap} 来替换实现类
 *
 * @author  Soup
 * @param <K>   枚举项code的类型
 */
public interface MapEnum<K> extends SelectEnum<K> {

    String CODE_KEY = "code";
    String DESC_KEY = "desc";

    default Map<String, Object> getMapVo() {
        return getMapVo(getCodeKey(), getDescKey());
    }

    default Map<String, Object> getMapVo(String codeKey, String descKey) {
        Map<String, Object> vo = getInitMap();
        vo.put(codeKey, getCode());
        vo.put(descKey, getDesc());
        extendVo(vo);
        return vo;
    }

    default Map<String, Object> getInitMap() {
        return new HashMap<>(4);
    }

    /**
     * 重写,添加扩展项
     */
    default void extendVo(Map<String, Object> vo) {}

    default String getCodeKey() {
        return CODE_KEY;
    }

    default String getDescKey() {
        return DESC_KEY;
    }

    /**
     * 将枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug,这里必须标识类型
     * Stream.<MapEnum<K>>of
     *
     * @param mapEnumClass   枚举类
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    static <T extends Enum<T> & MapEnum<K>, K> List<Map<String, Object>> list(Class<T> mapEnumClass) {
        T[] constants = mapEnumClass.getEnumConstants();
        return Stream.<MapEnum<K>>of(constants).map(MapEnum::getMapVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将枚举项转换为下拉列表
     * 自定义Map的编码和描述的键名称
     */
    static <T extends Enum<T> & MapEnum<K>, K> List<Map<String, Object>> list(String codeKey, String descKey, Class<T> mapEnumClass) {
        T[] constants = mapEnumClass.getEnumConstants();
        return Stream.<MapEnum<K>>of(constants).map(o -> o.getMapVo(codeKey, descKey)).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将给定的枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug,这里必须标识类型
     * Stream.<MapEnum<K>>of
     *
     * @param mapEnums   枚举项数组
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    @SafeVarargs
    static <T extends MapEnum<K>, K> List<Map<String, Object>> asList(T... mapEnums) {
        Objects.requireNonNull(mapEnums);
        return Stream.<MapEnum<K>>of(mapEnums).map(MapEnum::getMapVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将给定的枚举项转换为下拉列表
     * 自定义Map的编码和描述的键名称
     */
    @SafeVarargs
    static <T extends MapEnum<K>, K> List<Map<String, Object>> asList(String codeKey, String descKey, T... mapEnums) {
        Objects.requireNonNull(mapEnums);
        return Stream.<MapEnum<K>>of(mapEnums).map(o -> o.getMapVo(codeKey, descKey)).filter(Objects::nonNull).collect(Collectors.toList());
    }

}

测试类&使用示例

CodeEnumTestCase.java

import com.ilikesoup.instant.enums.CodeEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class CodeEnumTestCase {

    @Test
    public void testGetByCode() {
        // 通过code获取枚举实例,查找不到返回null
        Assertions.assertEquals(CodeEnum.getByCode("1", SampleCodeEnum.class), SampleCodeEnum.YES);
        Assertions.assertNull(CodeEnum.getByCode("2", SampleCodeEnum.class));
    }

    @Test
    public void testGetByCodeDef() {
        // 通过code获取枚举实例,设置默认值,如果查找不到返回默认值
        Assertions.assertEquals(CodeEnum.getByCode("1", SampleCodeEnum.class, SampleCodeEnum.NO), SampleCodeEnum.YES);
        Assertions.assertEquals(CodeEnum.getByCode("2", SampleCodeEnum.class, SampleCodeEnum.NO), SampleCodeEnum.NO);
    }

    @Test
    public void testContains() {
        // 查看枚举中是否存在指定 code 编码的实例
        Assertions.assertTrue(CodeEnum.contains("0", SampleCodeEnum.class));
        Assertions.assertFalse(CodeEnum.contains("2", SampleCodeEnum.class));
    }

    enum SampleCodeEnum implements CodeEnum<String> {
        YES("1"),
        NO("0");

        private final String code;

        SampleCodeEnum(String code) {
            this.code = code;
        }

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

    }

}

SelectEnumTestCase.java

import com.ilikesoup.instant.enums.SelectEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class SelectEnumTestCase {

    @Test
    public void testGetByCode() {
        // 通过code获取枚举实例,查找不到返回null
        Assertions.assertEquals(SelectEnum.getByCode(1, SampleSelectEnum.class), SampleSelectEnum.SUCCESS);
        Assertions.assertNull(SelectEnum.getByCode(99, SampleSelectEnum.class));
    }

    @Test
    public void testGetByCodeDef() {
        // 通过code获取枚举实例,设置默认值,如果查找不到返回默认值
        Assertions.assertEquals(SelectEnum.getByCode(2, SampleSelectEnum.class, SampleSelectEnum.SUCCESS), SampleSelectEnum.FAILURE);
        Assertions.assertEquals(SelectEnum.getByCode(99, SampleSelectEnum.class, SampleSelectEnum.SUCCESS), SampleSelectEnum.SUCCESS);
    }

    @Test
    public void testContains() {
        // 查看枚举中是否存在指定 code 编码的实例
        Assertions.assertTrue(SelectEnum.contains(1, SampleSelectEnum.class));
        Assertions.assertFalse(SelectEnum.contains(99, SampleSelectEnum.class));
    }

    @Test
    public void testGetDescByCode() {
        // 查看枚举中是指定 code 编码的实例描述,如果未找到实例返回 null
        Assertions.assertEquals(SelectEnum.getDescByCode(1, SampleSelectEnum.class), SampleSelectEnum.SUCCESS.getDesc());
        Assertions.assertNull(SelectEnum.getDescByCode(99, SampleSelectEnum.class));
    }

    @Test
    public void testGetDescByCodeDef() {
        // 查看枚举中是指定 code 编码的实例描述,给定默认值,如果未找到实例返回默认值
        Assertions.assertEquals(SelectEnum.getDescByCode(1, SampleSelectEnum.class, "ok"), SampleSelectEnum.SUCCESS.getDesc());
        Assertions.assertEquals(SelectEnum.getDescByCode(99, SampleSelectEnum.class, "ok"), "ok");
    }

    @Test
    public void testList() {
        Assertions.assertEquals(SelectEnum.list(SampleSelectEnum.class).size(), 2);
    }

    @Test
    public void testAsList() {
        Assertions.assertEquals(SelectEnum.asList(SampleSelectEnum.SUCCESS, SampleSelectEnum.FAILURE).size(), 2);
    }

    enum SampleSelectEnum implements SelectEnum<Integer> {
        SUCCESS(1, "成功"),
        FAILURE(2, "失败");

        private final Integer code;
        private final String desc;

        SampleSelectEnum(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }

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

        @Override
        public String getDesc() {
            return desc;
        }

    }

}

SelectEnumProTestCase.java


import com.ilikesoup.instant.enums.SelectEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class SelectEnumProTestCase {

    @Test
    public void testList() {
        Assertions.assertEquals(SelectEnum.list(ApplyStatus.class).size(), 5);
    }

    @Test
    public void testAsList() {
        Assertions.assertEquals(SelectEnum.asList(ApplyStatus.CREATED, ApplyStatus.NOT_PASSED).size(), 2);
        Assertions.assertEquals(SelectEnum.asList(ApplyStatus.DELETED, ApplyStatus.CREATED, ApplyStatus.NOT_PASSED).size(), 2);
    }

    /*
     * 示例:
     * 1、排除下拉项
     * 2、下拉项扩展属性
     */
    enum ApplyStatus implements SelectEnum<Integer> {
        DELETED(-1, "已删除") {
            @Override
            public SelectVo<Integer> getSelectVo() {
                // 将该项从默认列表中剔除
                return null;
            }
        },
        CREATED(1, "创建"),
        WAITING_AUDIT(2, "待审核"),
        PASSED(3, "已通过", true),
        NOT_PASSED(4, "未通过"),
        ARCHIVED(5, "已归档", true)
        ;

        private final Integer code;
        private final String desc;
        private final boolean checkedDef;

        ApplyStatus(Integer code, String desc) {
            this(code, desc, false);
        }

        ApplyStatus(Integer code, String desc, boolean checkedDef) {
            this.code = code;
            this.desc = desc;
            this.checkedDef = checkedDef;
        }

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

        @Override
        public String getDesc() {
            return desc;
        }

        public boolean isCheckedDef() {
            return checkedDef;
        }

        @Override
        public SelectVo<Integer> getSelectVo() {
            return new SelectVoPro(code, desc, checkedDef);
        }

        // 扩展默认的下拉vo, 添加属性
        public static class SelectVoPro extends SelectVo<Integer> {
            private boolean checkedDef;

            public SelectVoPro(Integer code, String desc, boolean checkedDef) {
                super(code, desc);
                this.checkedDef = checkedDef;
            }

            public boolean isCheckedDef() {
                return checkedDef;
            }

            @Override
            public String toString() {
                return "SelectVoPro{" +
                        "checkedDef=" + checkedDef +
                        "code='" + getCode() + '\'' +
                        ", desc='" + getDesc() + '\'' +
                        "} ";
            }
        }
    }

}

MapEnumTestCase.java

import com.ilikesoup.instant.enums.MapEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class MapEnumTestCase {

    @Test
    public void testGetMapVo() {
        Assertions.assertTrue(EnabledStatus.ENABLED.getMapVo().containsKey(MapEnum.CODE_KEY));
        Assertions.assertTrue(EnabledStatus.ENABLED.getMapVo().containsKey(MapEnum.DESC_KEY));
        Assertions.assertEquals(EnabledStatus.ENABLED.getMapVo().get(MapEnum.CODE_KEY), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(EnabledStatus.ENABLED.getMapVo().get(MapEnum.DESC_KEY), EnabledStatus.ENABLED.getDesc());
    }

    @Test
    public void testGetMapVo2() {
        Assertions.assertTrue(EnabledStatus.ENABLED.getMapVo("id", "name").containsKey("id"));
        Assertions.assertTrue(EnabledStatus.ENABLED.getMapVo("id", "name").containsKey("name"));
        Assertions.assertEquals(EnabledStatus.ENABLED.getMapVo("id", "name").get("id"), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(EnabledStatus.ENABLED.getMapVo("id", "name").get("name"), EnabledStatus.ENABLED.getDesc());
    }

    @Test
    public void testList() {
        List<Map<String, Object>> selectList = MapEnum.list(EnabledStatus.class);
        Assertions.assertEquals(selectList.size(), 3);
        Map<String, Object> vo = selectList.get(0);
        Assertions.assertTrue(vo.containsKey(MapEnum.CODE_KEY));
        Assertions.assertTrue(vo.containsKey(MapEnum.DESC_KEY));
        Assertions.assertEquals(vo.get(MapEnum.CODE_KEY), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(vo.get(MapEnum.DESC_KEY), EnabledStatus.ENABLED.getDesc());
    }

    @Test
    public void testList2() {
        List<Map<String, Object>> selectList = MapEnum.list("id", "name", EnabledStatus.class);
        Assertions.assertEquals(selectList.size(), 3);
        Map<String, Object> vo = selectList.get(0);
        Assertions.assertTrue(vo.containsKey("id"));
        Assertions.assertTrue(vo.containsKey("name"));
        Assertions.assertEquals(vo.get("id"), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(vo.get("name"), EnabledStatus.ENABLED.getDesc());
    }

    @Test
    public void testAsList() {
        List<Map<String, Object>> selectList = MapEnum.asList(EnabledStatus.ENABLED, EnabledStatus.DISABLED);
        Assertions.assertEquals(selectList.size(), 2);
        Map<String, Object> vo = selectList.get(0);
        Assertions.assertTrue(vo.containsKey(MapEnum.CODE_KEY));
        Assertions.assertTrue(vo.containsKey(MapEnum.DESC_KEY));
        Assertions.assertEquals(vo.get(MapEnum.CODE_KEY), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(vo.get(MapEnum.DESC_KEY), EnabledStatus.ENABLED.getDesc());
    }

    @Test
    public void testAsList2() {
        List<Map<String, Object>> selectList = MapEnum.asList("id", "name", EnabledStatus.ENABLED, EnabledStatus.DISABLED);
        Assertions.assertEquals(selectList.size(), 2);
        Map<String, Object> vo = selectList.get(0);
        Assertions.assertTrue(vo.containsKey("id"));
        Assertions.assertTrue(vo.containsKey("name"));
        Assertions.assertEquals(vo.get("id"), EnabledStatus.ENABLED.getCode());
        Assertions.assertEquals(vo.get("name"), EnabledStatus.ENABLED.getDesc());
    }

    enum EnabledStatus implements MapEnum<Integer> {
        ENABLED(1, "启用"),
        DISABLED(0, "禁用"),
        DELETED(-1, "删除")
        ;

        private final Integer code;
        private final String desc;

        EnabledStatus(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }

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

        @Override
        public String getDesc() {
            return desc;
        }

        @Override
        public Map<String, Object> getInitMap() {
            return new LinkedHashMap<>(4);
        }

    }
}

MapEnumProTestCase.java

import java.util.Map;

public class MapEnumProTestCase {

    @Test
    public void testRespJson() {
        Assertions.assertEquals(RespCode.SYSTEM_ERROR.getMapVo().getClass(), JSONObject.class);
    }


    enum RespCode implements MapEnum<Integer> {
        SYSTEM_ERROR(-1, "系统错误", 500),
        PARAM_ERROR(-2, "参数错误", 400),
        RESOURCE_NOT_FOUND(-3, "资源不存在", 404),
        BUSINESS_ERROR(-4, "业务错误", 200),
        SUCCESS(0, "处理成功", 200)
        ;

        private final Integer code;
        private final String desc;
        private final Integer respStatus;

        RespCode(Integer code, String desc, Integer respStatus) {
            this.code = code;
            this.desc = desc;
            this.respStatus = respStatus;
        }

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

        @Override
        public String getDesc() {
            return desc;
        }


        @Override
        public Map<String, Object> getInitMap() {
            return new JSONObject(4, true);
        }

        @Override
        public void extendVo(Map<String, Object> vo) {
            vo.put("status", respStatus);
        }

        @Override
        public String getDescKey() {
            return "msg";
        }
    }

}

IDEA类文件模板

getCode,getDesc最常用的实现就是用属性的getter方法,
此处提供IDEA类模板,避免重复编写相关代码

CodeEnum

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import ${PACKAGE_NAME}.CodeEnum;

#parse("File Header.java")
public enum ${NAME} implements CodeEnum<${CODE_TYPE}> {

    ;
    
    private final ${CODE_TYPE} code;
    
    ${NAME} (${CODE_TYPE} code) {
        this.code = code;
    }
    
    @Override
    public ${CODE_TYPE} getCode() {
        return code;
    }
    
}

SelectEnum

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import ${PACKAGE_NAME}.SelectEnum;

#parse("File Header.java")
public enum ${NAME} implements SelectEnum<${CODE_TYPE}> {

    ;
    
    private final ${CODE_TYPE} code;
    private final String desc;
    
    ${NAME} (${CODE_TYPE} code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    @Override
    public ${CODE_TYPE} getCode() {
        return code;
    }
    
    @Override
    public String getDesc() {
        return desc;
    }
    
}

MapEnum

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

import ${PACKAGE_NAME}.MapEnum;

#parse("File Header.java")
public enum ${NAME} implements MapEnum<${CODE_TYPE}> {

    ;
    
    private final ${CODE_TYPE} code;
    private final String desc;
    
    ${NAME} (${CODE_TYPE} code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    @Override
    public ${CODE_TYPE} getCode() {
        return code;
    }
    
    @Override
    public String getDesc() {
        return desc;
    }
    
}

枚举编码校验器

javax.validation 自定义枚举编码校验扩展
被校验的属性值必须在指定枚举中存在相同的编码
被校验的属性值为null,不校验约束

EnumCode.java

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 枚举编码约束校验注解
 */
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = EnumCodeConstraintValidator.class)
public @interface EnumCode {
    /**
     * 该枚举类必须实现 CodeEnum
     */
    Class<? extends Enum<?>> value();

    String message() default "{javax.validation.constraints.EnumCode.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

}

EnumCodeConstraintValidator.java

import com.ilikesoup.instant.enums.CodeEnum;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;

/**
 * 枚举项编码校验器
 * 被注释的对象,必须在枚举中可以找到对应的键 {@link Objects#equals(Object, Object)}
 *
 * 当被修饰值为 null 时,不进行校验并返回 true。如果需要进行非空约束,请附加使用 @NonNull
 *
 */
public class EnumCodeConstraintValidator implements ConstraintValidator<EnumCode, Object> {

    private Class<? extends Enum<?>> type;

    @Override
    public void initialize(EnumCode constraintAnnotation) {
        type = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(value == null) {
            return true;
        }
        if(CodeEnum.class.isAssignableFrom(type)) {
            for (Enum<?> enumConstant : type.getEnumConstants()) {
                if(Objects.equals(((CodeEnum) enumConstant).getCode(), value)) {
                    return true;
                }
            }
            return false;
        }
        return false;
    }

}
 
原文:https://www.cnblogs.com/ILikeSoup/p/instant_soup_enums.html


相关教程