enhance EnumConverter

This commit is contained in:
Looly 2020-03-02 13:41:38 +08:00
parent b740fe6480
commit c5e4d0ef34
6 changed files with 163 additions and 10 deletions

View File

@ -13,6 +13,8 @@
* 【http 】 支持patch方法issue#666@Github
* 【crypto】 BCUtil支持更加灵活的密钥类型增加writePemObject方法
* 【core 】 增加ServiceLoaderUtil
* 【core 】 增加EnumUtil.getEnumAt方法
* 【core 】 增强EnumConvert判断能力issue#I17082@Gitee
### Bug修复

View File

@ -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 extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) {
return (new GenericEnumConverter<>(clazz)).convertQuietly(value, defaultValue);
return (E) (new EnumConverter(clazz)).convertQuietly(value, defaultValue);
}
/**

View File

@ -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<Object> {
private static final long serialVersionUID = 1L;
private static final Map<Class<?>, Map<Class<?>, 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<Object> {
@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<Class<?>, Method> valueOfMethods = getValueOfMethods(enumClass);
if (MapUtil.isNotEmpty(valueOfMethods)) {
final Class<?> valueClass = value.getClass();
for (Map.Entry<Class<?>, 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<Class<?>, Method> getValueOfMethods(Class<?> enumClass) {
Map<Class<?>, 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;
}
}

View File

@ -8,7 +8,9 @@ import cn.hutool.core.convert.AbstractConverter;
* @param <E> 枚举类类型
* @author Looly
* @since 4.0.2
* @deprecated 请使用{@link EnumConverter}
*/
@Deprecated
public class GenericEnumConverter<E extends Enum<E>> extends AbstractConverter<E> {
private static final long serialVersionUID = 1L;
@ -25,7 +27,13 @@ public class GenericEnumConverter<E extends Enum<E>> extends AbstractConverter<E
@Override
protected E convertInternal(Object value) {
return Enum.valueOf(enumClass, convertToStr(value));
//noinspection unchecked
E enumValue = (E) EnumConverter.tryConvertEnum(value, this.enumClass);
if(null == enumValue && false == value instanceof String){
// 最后尝试valueOf转换
enumValue = Enum.valueOf(this.enumClass, convertToStr(value));
}
return enumValue;
}
@Override

View File

@ -50,6 +50,20 @@ public class EnumUtil {
return null != e ? e.name() : null;
}
/**
* 字符串转枚举调用{@link Enum#valueOf(Class, String)}
*
* @param <E> 枚举类型泛型
* @param enumClass 枚举类
* @param index 枚举索引
* @return 枚举值null表示无此对应枚举
* @since 5.1.6
*/
public static <E extends Enum<E>> E getEnumAt(Class<E> 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 <E extends Enum<E>> LinkedHashMap<String, E> getEnumMap(final Class<E> enumClass) {
final LinkedHashMap<String, E> map = new LinkedHashMap<String, E>();
final LinkedHashMap<String, E> map = new LinkedHashMap<>();
for (final E e : enumClass.getEnumConstants()) {
map.put(e.name(), e);
}
@ -248,7 +262,7 @@ public class EnumUtil {
/**
* 判断某个值是不存在枚举中
*
* @param <E> 枚举类型
* @param <E> 枚举类型
* @param enumClass 枚举类
* @param val 需要查找的值
* @return 是否不存在

View File

@ -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;
}
}
}