程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java枚舉的反向查找

Java枚舉的反向查找

編輯:關於JAVA

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原則,重復代碼地方一般都有一定的壞味道。

查看本欄目

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(依賴注入)的感覺?!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved