副标题#e#
java的列举经常被用来替代常量值,每个列举值代表一个特定的常数。
在反序列化时有经常需要用到常数到列举的转换,这就涉及到列举的反向查找。
1、从列举名反向查找列举
这种要领是最先利用也最为轻便的
可以用到列举的静态要领valueOf(String name)
valueOf要领为内置要领,利用轻便,但在查找不到列举时会抛出异常。
熟悉异常的同学大概知道异常抛出时,需要收集虚拟机的挪用仓库上下文信息,对机能影响较大。
利用时,经常需要利用这么一个反序列化查找要领
E find(String name, E defaultValue),在查找不到时可以或许返回一个默认值而不是抛出异常
2、从列举值所包括的描写值反向查找列举
譬喻这种列举界说
public enum SomeEnum{ A("ADes", 1), B("BDes", 2), unknown("UNKNWONDes", 3); private string des; private int order; private SomeEnum(string des, int order){ this.des = des; this.order = order; } }
此时可以在列举类中增加一个HashMap,并在类加载时初始化好。
public enum SomeEnum{ A("ADes"), B("BDes"), unknown("UNKNWONDes"); private string des; private static final map lookup = new hashmap(); static { for(SomeEnum e : EnumSet.allOf(SomeEnum.class)){ lookup.put(e.des, e); } } private SomeEnum(string des){ this.des = des; } public static SomeEnum find(string des, SomeEnum defaultValue){ SomeEnum value = lookup.get(des); if(value == null){ return defaultValue; } return value; } }
3、进一步,假如列举中有多个描写值,而且描写值范例纷歧样
这时初始化代码和查找代码需要写多遍。
public enum SomeEnum{ A("ADes", 1), B("BDes", 2), unknown("UNKNWONDes", 3); private string des; private int order; private static final map lookup = new hashmap(); private static final map lookup_int = new hashmap(); static { for(SomeEnum e : EnumSet.allOf(SomeEnum.class)){ lookup.put(e.des, e); lookup_int.put(e.order, e); } } private SomeEnum(string des, int order){ this.des = des; this.order = order; } public static SomeEnum find(string des, SomeEnum defaultValue){ SomeEnum value = lookup.get(des); if(value == null){ return defaultValue; } return value; } public static SomeEnum find(int order, SomeEnum defaultValue){ SomeEnum value = lookup_int.get(order); if(value == null){ return defaultValue; } return value; } }
不少代码有反复,
列举类需要本身实现反向查找映射干系在HashMap中的初始化。
按照DRY原则,反复代码处所一般都有必然的坏味道。
查察本栏目
#p#副标题#e#
4、改造
列举的反向查找其实只需存眷两件工作,
a 提供列举值描写到列举值的映射
b 能从列举值描写查找到列举值,查找不到能提供默认值
据此,可以提炼出列举反向查找的辅佐类:
public class EnumFindHelper<T extends Enum<T>, K> { protected Map<K, T> map = new HashMap<K, T>(); public EnumFindHelper(Class<T> clazz, EnumKeyGetter<T, K> keyGetter) { try { for (T enumValue : EnumSet.allOf(clazz)) { map.put(keyGetter.getKey(enumValue), enumValue); } } catch (Exception e) { //eror handler } } public T find(K key, T defautValue) { T value = map.get(key); if (value == null) { value = defautValue; } return value; } }
这里需要一个接口界说:
public static interface EnumKeyGetter<T extends Enum<T>, K> { K getKey(T enumValue); }
查找辅佐类EnumFindHelper结构时需要EnumKeyGetter来提供列举值到列举范例的的对应干系。
界说列举类时实现EnumKeyGetter接口,并传入EnumFindHelper。
修改后的列举界说大概为:
public enum SomeEnum{ A("ADes", 1), B("BDes", 2), unknown("UNKNWONDes", 3); private String des; private int order; private SomeEnum(string des, int order){ this.des = des; this.order = order; } static final EnumFindHelper<SomeEnum, String> desptHelper = new EnumFindHelper<SomeEnum, String>( SomeEnum.class, new DesptGetter()); static final EnumFindHelper<SomeEnum, Integer> orderHelper = new EnumFindHelper<SomeEnum, Integer>( SomeEnum.class, new OrderKeyGetter()); static class DesptGetter implements EnumKeyGetter<SomeEnum, String> { @Override public String getKey(SomeEnum enumValue) { return enumValue.des; } } static class OrderKeyGetter implements EnumKeyGetter<SomeEnum, Integer> { @Override public String Integer(SomeEnum enumValue) { return enumValue.order; } } public static SomeEnum find(string despt, SomeEnum defaultValue){ return desptHelper.find(des, defaultValue); } public static SomeEnum find(int order, SomeEnum defaultValue){ return orderHelper.find(des, defaultValue); } }
EnumFindHelper内部类的界说有些繁琐,不外java8之后用lambda实现后会轻便一些。
5 总结
每个列举类的反向查找成果委托一个辅佐类来实现,从列举类的结构上来看,较量雷同GOF的桥接模式。
而从委托类的实现查找成果来看,需要一个EnumKeyGetter接口,每种差异的接话柄现差异的查找方法,有按照描写字符查找,有按照列举顺序值查找。
这又有些雷同GOF的计策模式。
虽然不需要去纠结于哪种设计模式,代码的设计就是要求代码不冗余,成果的重用,依赖的解耦。
上面的设计中:
1)操作EnumFindHelper做到列举查找的算法重用,操作一个map提供列举描写到列举值的查找。
2)EnumFindHelper初始化时需要结构好查找用的map,所以会依赖于详细的列举界说。
这里抽象出EnumKeyGetter,EnumFindHelper由依赖于详细的列举类改为依赖于EnumKeyGetter接口,在结构时注入了EnumKeyGetter的实现。
这么看起来是不是有些DPI(依赖注入)的感受?!