问题场景
- 通过枚举的编码查询枚举项
- 将枚举项返回给前端作为下拉框选项
- 将枚举项作为结果返回
问题扩展
- 通过枚举的编码查询枚举项 -> 检查指定枚举中是否存在某个编码(检查枚举参数的合法性)
- 通过枚举的编码查询枚举项 -> 如果找不到给默认值(非法参数兼容,避免null判断)
- 将枚举项返回给前端作为下拉框选项 -> 应支持自定义选择枚举项
- 将枚举项作为结果返回 -> 应提供支持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;
}
}