SpringBoot中枚举属性校验通用方法的优化实现问询
问题描述
在SpringBoot项目开发中,经常需要编写代码判断API传入参数是否为枚举值的预期属性。例如下面的枚举类,其中isAttributeEquals方法用于判断传入值是否匹配枚举的str属性:
@Getter public enum Sample2Enum { A("a"), B("b"), C("c"), ; private final String str; Sample2Enum(String str) { this.str = str; } public static boolean isAttributeEquals(String value) { boolean isAttributeEquals = false; for (Sample2Enum sample2Enum : Sample2Enum.values()) { if (sample2Enum.getStr().equals(value)) { isAttributeEquals = true; break; } } return isAttributeEquals; } }
这类判断逻辑在多个枚举类中重复度极高,因此希望实现一个通用方法,将判断逻辑交由调用方传入,替代当前硬编码方法名+反射的实现方式(当前反射实现如下):
/** * 判断给定参数是否为指定枚举类中枚举值的属性 * @param enumClass 目标枚举类 * @param methodName 要调用的方法名 * @param values 需要校验的参数 * @return 匹配返回true,否则返回false */ public static <T> boolean isValidEnumAttribute(Class<T> enumClass, String methodName, Object... values) throws Exception { T[] enums = enumClass.getEnumConstants(); Class<?>[] params = new Class[values.length]; for (int i = 0; i < values.length; i++) { params[i] = values[i].getClass(); } Method method = enumClass.getMethod(methodName, params); boolean isEnum = false; for (T enumValue : enums) { isEnum = (boolean) method.invoke(enumValue, values); if (isEnum) break; } return isEnum; }
期望能通过方法引用的方式传递判断逻辑,比如调用时写成:
EnumUtils.isValidEnumAttribute(Sample2Enum.class, Sample2Enum::isAttributeEquals2, "a");
优化方案
方案一:函数式接口实现方法引用传递
Java 8及以上的函数式接口可以完美满足需求,无需反射,编译期即可校验逻辑合法性。
1. 定义函数式接口
@FunctionalInterface public interface EnumAttributeMatcher<T> { boolean matches(T enumInstance, Object... args); }
2. 实现通用工具方法
public class EnumUtils { public static <T extends Enum<T>> boolean isValidEnumAttribute(Class<T> enumClass, EnumAttributeMatcher<T> matcher, Object... values) { for (T enumInstance : enumClass.getEnumConstants()) { if (matcher.matches(enumInstance, values)) { return true; } } return false; } }
3. 枚举类中定义判断逻辑并调用
在枚举类中定义实例方法作为自定义判断逻辑:
@Getter public enum Sample2Enum { A("a"), B("b"), C("c"), ; private final String str; Sample2Enum(String str) { this.str = str; } // 自定义逻辑:排除B的匹配 public boolean isAttributeEquals2(String value) { if (this == Sample2Enum.B) { return false; } return this.getStr().equals(value); } // 自定义逻辑:直接匹配str属性 public boolean isAttributeEquals3(String value) { return this.getStr().equals(value); } }
调用时通过方法引用传递逻辑:
// 调用示例 boolean result1 = EnumUtils.isValidEnumAttribute(Sample2Enum.class, Sample2Enum::isAttributeEquals2, "a"); boolean result2 = EnumUtils.isValidEnumAttribute(Sample2Enum.class, Sample2Enum::isAttributeEquals3, "b");
如果需要支持多参数判断,只需在枚举的实例方法中对应参数即可:
// 枚举中多参数判断方法 public boolean matchesMultiAttr(String strVal, int intVal) { return this.getStr().equals(strVal) && this.ordinal() == intVal; } // 调用 boolean result3 = EnumUtils.isValidEnumAttribute(Sample2Enum.class, Sample2Enum::matchesMultiAttr, "a", 0);
方案二:简化版——Stream+Predicate直接实现
如果不需要通用到所有枚举,也可以直接用Stream简化逻辑,无需额外工具类:
// 判断"a"是否匹配(排除B) boolean valid = Arrays.stream(Sample2Enum.values()) .anyMatch(Sample2Enum::isAttributeEquals2); // 直接内联判断逻辑,无需枚举内定义方法 boolean valid2 = Arrays.stream(Sample2Enum.values()) .anyMatch(e -> !e.equals(Sample2Enum.B) && e.getStr().equals("a"));
方案对比与性能说明
- 反射方案:存在硬编码方法名的风险,编译期无法校验方法存在性,且反射有一定性能损耗,还需处理异常。
- 函数式接口方案:编译期校验方法引用合法性,性能与直接遍历枚举调用方法完全一致,无额外损耗,代码可读性与可维护性更高。
内容的提问来源于stack exchange,提问作者Pai
相关产品推荐
相关产品推荐

