diff --git a/CHANGELOG.md b/CHANGELOG.md index a73c65485..fc17f04f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ * 【http 】 支持patch方法(issue#666@Github) * 【crypto】 BCUtil支持更加灵活的密钥类型,增加writePemObject方法 * 【core 】 增加ServiceLoaderUtil +* 【core 】 增加EnumUtil.getEnumAt方法 +* 【core 】 增强EnumConvert判断能力(issue#I17082@Gitee) ### Bug修复 diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java index 5981608d2..bfc050665 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java @@ -10,7 +10,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; import cn.hutool.core.convert.impl.CollectionConverter; -import cn.hutool.core.convert.impl.GenericEnumConverter; +import cn.hutool.core.convert.impl.EnumConverter; import cn.hutool.core.convert.impl.MapConverter; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.TypeReference; @@ -522,7 +522,7 @@ public class Convert { * @return Enum */ public static > E toEnum(Class clazz, Object value, E defaultValue) { - return (new GenericEnumConverter<>(clazz)).convertQuietly(value, defaultValue); + return (E) (new EnumConverter(clazz)).convertQuietly(value, defaultValue); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index 3e9f26165..1b4d5618b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java @@ -1,22 +1,35 @@ package cn.hutool.core.convert.impl; import cn.hutool.core.convert.AbstractConverter; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ModifierUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * 无泛型检查的枚举转换器 - * + * * @author Looly * @since 4.0.2 */ -@SuppressWarnings({ "unchecked", "rawtypes" }) +@SuppressWarnings({"unchecked", "rawtypes"}) public class EnumConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + private static final Map, Map, Method>> VALUE_OF_METHOD_CACHE = new ConcurrentHashMap<>(); + private Class enumClass; - + /** * 构造 - * + * * @param enumClass 转换成的目标Enum类 */ public EnumConverter(Class enumClass) { @@ -25,11 +38,71 @@ public class EnumConverter extends AbstractConverter { @Override protected Object convertInternal(Object value) { - return Enum.valueOf(enumClass, convertToStr(value)); + Enum enumValue = tryConvertEnum(value, this.enumClass); + if(null == enumValue && false == value instanceof String){ + // 最后尝试valueOf转换 + enumValue = Enum.valueOf(this.enumClass, convertToStr(value)); + } + return enumValue; } @Override public Class getTargetType() { return this.enumClass; } + + /** + * 尝试找到类似转换的静态方法调用实现转换 + * + * @param value 被转换的值 + * @param enumClass enum类 + * @return 对应的枚举值 + */ + protected static Enum tryConvertEnum(Object value, Class enumClass) { + Enum enumResult = null; + if (value instanceof Integer) { + enumResult = EnumUtil.getEnumAt(enumClass, (Integer)value); + } else if (value instanceof String) { + try { + enumResult = Enum.valueOf(enumClass, (String) value); + } catch (IllegalArgumentException e) { + //ignore + } + } + + // 尝试查找其它用户自定义方法 + if(null == enumResult){ + final Map, Method> valueOfMethods = getValueOfMethods(enumClass); + if (MapUtil.isNotEmpty(valueOfMethods)) { + final Class valueClass = value.getClass(); + for (Map.Entry, Method> entry : valueOfMethods.entrySet()) { + if (ClassUtil.isAssignable(entry.getKey(), valueClass)) { + enumResult = ReflectUtil.invokeStatic(entry.getValue(), value); + } + } + } + } + + return enumResult; + } + + /** + * 获取用于转换为enum的所有static方法 + * + * @param enumClass 枚举类 + * @return 转换方法map + */ + private static Map, Method> getValueOfMethods(Class enumClass) { + Map, Method> valueOfMethods = VALUE_OF_METHOD_CACHE.get(enumClass); + if (null == valueOfMethods) { + valueOfMethods = Arrays.stream(enumClass.getMethods()) + .filter(ModifierUtil::isStatic) + .filter(m -> m.getReturnType() == enumClass) + .filter(m -> m.getParameterCount() == 1) + .filter(m -> false == "valueOf".equals(m.getName())) + .collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m, (existing, replacement) -> existing)); + VALUE_OF_METHOD_CACHE.put(enumClass, valueOfMethods); + } + return valueOfMethods; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/GenericEnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/GenericEnumConverter.java index 36676fa3b..179717041 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/GenericEnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/GenericEnumConverter.java @@ -8,7 +8,9 @@ import cn.hutool.core.convert.AbstractConverter; * @param 枚举类类型 * @author Looly * @since 4.0.2 + * @deprecated 请使用{@link EnumConverter} */ +@Deprecated public class GenericEnumConverter> extends AbstractConverter { private static final long serialVersionUID = 1L; @@ -25,7 +27,13 @@ public class GenericEnumConverter> extends AbstractConverter 枚举类型泛型 + * @param enumClass 枚举类 + * @param index 枚举索引 + * @return 枚举值,null表示无此对应枚举 + * @since 5.1.6 + */ + public static > E getEnumAt(Class enumClass, int index) { + final E[] enumConstants = enumClass.getEnumConstants(); + return index < enumConstants.length ? enumConstants[index] : null; + } + /** * 字符串转枚举,调用{@link Enum#valueOf(Class, String)} * @@ -206,7 +220,7 @@ public class EnumUtil { * @since 4.0.2 */ public static > LinkedHashMap getEnumMap(final Class enumClass) { - final LinkedHashMap map = new LinkedHashMap(); + final LinkedHashMap map = new LinkedHashMap<>(); for (final E e : enumClass.getEnumConstants()) { map.put(e.name(), e); } @@ -248,7 +262,7 @@ public class EnumUtil { /** * 判断某个值是不存在枚举中 * - * @param 枚举类型 + * @param 枚举类型 * @param enumClass 枚举类 * @param val 需要查找的值 * @return 是否不存在 diff --git a/hutool-core/src/test/java/cn/hutool/core/convert/EnumConvertTest.java b/hutool-core/src/test/java/cn/hutool/core/convert/EnumConvertTest.java new file mode 100644 index 000000000..2271cefd2 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/convert/EnumConvertTest.java @@ -0,0 +1,56 @@ +package cn.hutool.core.convert; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Enum转换单元测试 + */ +public class EnumConvertTest { + + @Test + public void convertTest(){ + TestEnum bbb = Convert.convert(TestEnum.class, "BBB"); + Assert.assertEquals(TestEnum.B, bbb); + + bbb = Convert.convert(TestEnum.class, 22); + Assert.assertEquals(TestEnum.B, bbb); + } + + @Test + public void toEnumTest(){ + TestEnum ccc = Convert.toEnum(TestEnum.class, "CCC"); + Assert.assertEquals(TestEnum.C, ccc); + + ccc = Convert.toEnum(TestEnum.class, 33); + Assert.assertEquals(TestEnum.C, ccc); + } + + enum TestEnum { + A, B, C; + + public static TestEnum parse(String str) { + switch (str) { + case "AAA": + return A; + case "BBB": + return B; + case "CCC": + return C; + } + return null; + } + + public static TestEnum parseByNumber(int i) { + switch (i) { + case 11: + return A; + case 22: + return B; + case 33: + return C; + } + return null; + } + } +}