Java中如何根据值获取泛型枚举对象及EnumUtil的必要性
一、抽取EnumUtil的必要性
比如说,我在业务中定义了一个表示“加密类型”的枚举类 EncryptType:
import cn.hutool.core.util.StrUtil;
import com.suning.tech.exception.GatewayRuntimeException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Optional;
/**
* 功能描述:加密类型
*/
@AllArgsConstructor
@Getter
public enum EncryptType {
NONE (0), // 不加密
AES(1), // 1:aes
RSA(2), // 2:rsa
XXTEA(3); // 3:xxtea
int type;
public static EncryptType parseInt(int typeVal) {
Optional<EncryptType> result = Arrays.stream(values()) // values() 可以获取当前枚举类所有枚举常量
.filter(t -> t.getType() == typeVal) // 判断相等的条件
.findFirst();
if (result.isPresent()) {
return result.get();
} else {
throw new GatewayRuntimeException(StrUtil.format("No EncryptType matches type value {}", typeVal)); // NOSONAR
}
}
}
这样做的好处是
- 避免客户端代码中的魔法值;
- 客户端代码更加清晰明了;
EncryptType encryptType = EncryptType.parseInt(type);
if (EncryptType.XXTEA.equals(encryptType)) {
// XXTEA 加密
} else if (EncryptType.AES.equals(encryptType)) {
// AES 加密
} else if (EncryptType.RSA.equals(encryptType)) {
// RSA 加密
}
你可以想象一下,如果直接拿 int 类型的type 和 1,2,3 做比较,代码看起来会有多糟糕。
但是,枚举类型使用得多了以后,需要在每一个枚举类中,都写一段 parseInt 类型的代码,那得多糟心!
比如,我的项目中,就有这么多枚举类型,都需要增加解析方法!
这不够简洁啊!!! 于是,我尝试用泛型来抽取这段方法。
二、我的方案
import cn.hutool.core.util.StrUtil;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
/**
* 功能描述: 枚举工具类
*
* @author geekziyu
* @version 1.0.0
*/
public class EnumUtil {
public static <E extends Enum<E>> E valueOf(Class<E> e, String typeValue) {
E[] values = e.getEnumConstants();
Optional<E> result = Arrays.stream(values)
.filter(t -> String.valueOf(getTypeValue(t)).equals(typeValue)) // 用字符串形式比较
.findFirst();
if (result.isPresent()) {
return result.get();
} else {
throw new RuntimeException(StrUtil.format("No {} matches type value {}", e.getTypeName(), typeValue)); // NOSONAR
}
}
public static <E extends Enum<E>> E valueOf(Class<E> e, int typeValue) {
return valueOf(e, String.valueOf(typeValue));
}
private static <E extends Enum<E>> Object getTypeValue(E e) {
Class<? extends Enum> typeClass = e.getClass();
try {
Method getter = typeClass.getMethod("getTypeValue");
ReflectionUtils.makeAccessible(getter);
return getter.invoke(e);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(StrUtil.format("No such method named 'getTypeValue' in Enum {}!", typeClass.getTypeName())); // NOSONAR
} catch (IllegalAccessException ex) {
throw new RuntimeException(StrUtil.format("Inaccessible is the method 'getTypeValue' in Enum {} !", typeClass.getTypeName()), ex.getCause()); // NOSONAR
} catch (InvocationTargetException ex) {
throw new RuntimeException(StrUtil.format("Exception occurred to method 'getTypeValue' in Enum {}!", typeClass.getTypeName()), ex.getCause()); // NOSONAR
}
}
}
当然,我这个工具类也有局限性,那就是我限定了枚举类中,必须要有一个名为 typeValue 的字段且必须要有 getTypeValue 方法!
三、hutool的解决方案
public static <E extends Enum<E>> E likeValueOf(Class<E> enumClass, Object value) {
if (value instanceof CharSequence) {
value = value.toString().trim();
}
final Field[] fields = ReflectUtil.getFields(enumClass);
final Enum<?>[] enums = enumClass.getEnumConstants();
String fieldName;
for (Field field : fields) {
fieldName = field.getName();
if (field.getType().isEnum() || "ENUM$VALUES".equals(fieldName) || "ordinal".equals(fieldName)) {
// 跳过一些特殊字段
continue;
}
for (Enum<?> enumObj : enums) {
if (ObjectUtil.equal(value, ReflectUtil.getFieldValue(enumObj, field))) {
return (E) enumObj;
}
}
}
return null;
}
以 EncryptType 为例,有以下字段:
字段 | field.getType().isEnum() | 类型 | fieldName |
---|---|---|---|
EncryptType.NONE | true | EncryptType | "NONE" |
EncryptType.AES | true | EncryptType | "AES" |
EncryptType.RSA | true | EncryptType | "RSA" |
EncryptType.XXTEA | true | EncryptType | "XXTEA" |
EncryptType.type | false | int | "type" |
EncryptType.$VALUES |
false | EncryptType[] |
"$VALUES" |
EncryptType.name | false | String | "name" |
EncryptType.ordinal | false | int | "ordinal" |
ObjectUtil.equal 的最普遍的“相等”判断依据是 (a == b) || (a != null && a.equals(b))
。