Java 枚舉常用於替代常量,但在反序列化、配置映射等場景下,往往需要根據名稱、描述值或序數反向查找枚舉實例。本文由淺入深介紹四種反向查找模式,並給出可復用的通用解決方案。
枚舉內置靜態方法 valueOf(String name) 可直接根據名稱獲取實例,但查找失敗會拋出 IllegalArgumentException,且異常堆疊捕獲有性能開銷。推薦封裝安全查找方法:
public static E findByName(String name, E defaultValue) {
try {
return Enum.valueOf(E.class, name);
} catch (IllegalArgumentException e) {
return defaultValue;
}
}
例如枚舉攜帶描述字符串:
public enum SomeEnum {
A("ADes"), B("BDes"), UNKNOWN("UNKNOWNDes");
private final String des;
private SomeEnum(String des) { this.des = des; }
}
可在枚舉內部構建 HashMap<String, SomeEnum> 映射,靜態代碼塊初始化:
private static final Map<String, SomeEnum> LOOKUP = new HashMap<>();
static {
for (SomeEnum e : EnumSet.allOf(SomeEnum.class)) {
LOOKUP.put(e.des, e);
}
}
public static SomeEnum findByDes(String des, SomeEnum defaultValue) {
SomeEnum v = LOOKUP.get(des);
return v != null ? v : defaultValue;
}
若枚舉同時包含字符串描述與整型序數:
public enum SomeEnum {
A("ADes", 1), B("BDes", 2), UNKNOWN("UNKNOWNDes", 3);
private final String des;
private final int order;
private SomeEnum(String des, int order) { this.des = des; this.order = order; }
// 需維護兩個 Map,代碼重複
}
每增加一個查找屬性就需新增 Map 與查找方法,違反 DRY 原則,產生代碼惡味。
提煉通用查找邏輯,引入 EnumFindHelper 與 EnumKeyGetter 策略接口:
public class EnumFindHelper<T extends Enum<T>, K> {
private final Map<K, T> map = new HashMap<>();
public EnumFindHelper(Class<T> clazz, EnumKeyGetter<T, K> keyGetter) {
for (T enumValue : EnumSet.allOf(clazz)) {
map.put(keyGetter.getKey(enumValue), enumValue);
}
}
public T find(K key, T defaultValue) {
T v = map.get(key);
return v != null ? v : defaultValue;
}
}
public interface EnumKeyGetter<T extends Enum<T>, K> {
K getKey(T enumValue);
}
public enum SomeEnum {
A("ADes", 1), B("BDes", 2), UNKNOWN("UNKNOWNDes", 3);
private final String des;
private final int order;
private SomeEnum(String des, int order) { this.des = des; this.order = order; }
private static final EnumFindHelper<SomeEnum, String> DES_HELPER =
new EnumFindHelper<>(SomeEnum.class, e -> e.des);
private static final EnumFindHelper<SomeEnum, Integer> ORDER_HELPER =
new EnumFindHelper<>(SomeEnum.class, e -> e.order);
public static SomeEnum findByDes(String des, SomeEnum def) {
return DES_HELPER.find(des, def);
}
public static SomeEnum findByOrder(int order, SomeEnum def) {
return ORDER_HELPER.find(order, def);
}
}
Java 8+ 可使用 Lambda 簡化 EnumKeyGetter 實現,代碼更簡潔。
EnumKeyGetter 定義查找鍵提取策略,不同屬性對應不同實現EnumKeyGetter,解除對枚舉類的硬依賴通用輔助類模式實現了:
此設計符合高內聚、低耦合原則,適合作為項目標準枚舉查找基礎設施。