一、抽取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))
。