Java動態生成枚舉類型


原文:https://blog.csdn.net/qq_30038111/article/details/80816286

枚舉一般會用在一些狀態的表示、單例實現等場景上。有的時候可能希望將枚舉類的值放到配置文件、或者數據庫中,由項目啟動或者運行時動態確定枚舉值,這個時候就需要動態添加枚舉值。
使用動態添加枚舉值時建議在枚舉類上增加getEnum方法,增加對枚舉的緩沖,減少對枚舉類的改變,最好是在項目啟動時就把枚舉類初始化好
舉個栗子:
 1 import xxx.DynamicEnumUtil;
 2 import java.util.EnumSet;
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 public enum  YesNoEnum {
 7     YES(1,"yes"),
 8     NO(0,"no");
 9 
10     private Integer value;
11     private String name;
12     private static Map<Integer, YesNoEnum> enumMap = new HashMap<>();
13 
14     static{
15         //  可以在這里加載枚舉的配置文件 比如從 properties  數據庫中加載
16         //  加載完后 使用DynamicEnumUtil.addEnum 動態增加枚舉值
17         //  然后正常使用枚舉即可
18         EnumSet<YesNoEnum> set = EnumSet.allOf(YesNoEnum.class);
19         for (YesNoEnum each: set ) {
20             // 增加一個緩存 減少對枚舉的修改
21             enumMap.put(each.value, each);
22         }
23     }
24 
25     YesNoEnum(Integer value ,String name){
26         this.value = value;
27         this.name = name;
28     }
29 
30     public Integer getValue() {
31         return value;
32     }
33 
34     public String getName() {
35         return name;
36     }
37 
38     // 根據關鍵字段獲取枚舉值  可以在這里做一些修改 來達到動態添加的效果
39     public YesNoEnum getEnum(Integer value){
40         // 這里可以做一些修改  比如若從 enumMap 中沒有取得 則加載配置動態添加
41         return enumMap.get(value);
42     }
43 
44     //  動態添加方法  添加完后加入緩存 減少對枚舉的修改
45     public YesNoEnum addEnum(String enumName, Integer value,String name){
46         YesNoEnum yesNoEnum = DynamicEnumUtil.addEnum(YesNoEnum.class, enumName, new Class[]{Integer.class, String.class}, new Object[]{value, name});
47         enumMap.put(value,yesNoEnum);
48         return yesNoEnum;
49     }
50 }
工具類如下:
  1 import sun.reflect.ConstructorAccessor;
  2 import sun.reflect.FieldAccessor;
  3 import sun.reflect.ReflectionFactory;
  4 
  5 import java.lang.reflect.AccessibleObject;
  6 import java.lang.reflect.Array;
  7 import java.lang.reflect.Field;
  8 import java.lang.reflect.Modifier;
  9 import java.util.ArrayList;
 10 import java.util.Arrays;
 11 import java.util.List;
 12 
 13 public class DynamicEnumUtil {
 14  
 15    private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
 16  
 17    private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException,
 18            IllegalAccessException {
 19  
 20        // let's make the field accessible
 21        field.setAccessible(true);
 22  
 23        // next we change the modifier in the Field instance to
 24        // not be final anymore, thus tricking reflection into
 25        // letting us modify the static final field
 26        Field modifiersField = Field.class.getDeclaredField("modifiers");
 27        modifiersField.setAccessible(true);
 28        int modifiers = modifiersField.getInt(field);
 29  
 30        // blank out the final bit in the modifiers int
 31        modifiers &= ~Modifier.FINAL;
 32        modifiersField.setInt(field, modifiers);
 33  
 34        FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
 35        fa.set(target, value);
 36    }
 37  
 38    private static void blankField(Class<?> enumClass, String fieldName) throws NoSuchFieldException,
 39            IllegalAccessException {
 40        for (Field field : Class.class.getDeclaredFields()) {
 41            if (field.getName().contains(fieldName)) {
 42                AccessibleObject.setAccessible(new Field[] { field }, true);
 43                setFailsafeFieldValue(field, enumClass, null);
 44                break;
 45            }
 46        }
 47    }
 48  
 49    private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException {
 50        blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
 51        blankField(enumClass, "enumConstants"); // IBM JDK
 52    }
 53  
 54    private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
 55            throws NoSuchMethodException {
 56        Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
 57        parameterTypes[0] = String.class;
 58        parameterTypes[1] = int.class;
 59        System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
 60        return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
 61    }
 62  
 63    private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes,
 64            Object[] additionalValues) throws Exception {
 65        Object[] parms = new Object[additionalValues.length + 2];
 66        parms[0] = value;
 67        parms[1] = Integer.valueOf(ordinal);
 68        System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
 69 //       parms[1] = parms[parms.length-1];
 70        return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
 71    }
 72  
 73    /**
 74     * Add an enum instance to the enum class given as argument
 75     *
 76     * @param <T> the type of the enum (implicit)
 77     * @param enumType the class of the enum to be modified
 78     * @param enumName the name of the new enum instance to be added to the class.
 79     */
 80    @SuppressWarnings("unchecked")
 81    public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes, Object[] additionalValues) {
 82  
 83        // 0. Sanity checks
 84        if (!Enum.class.isAssignableFrom(enumType)) {
 85            throw new RuntimeException("class " + enumType + " is not an instance of Enum");
 86        }
 87  
 88        // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
 89        Field valuesField = null;
 90        Field[] fields = enumType.getDeclaredFields();
 91        for (Field field : fields) {
 92            if (field.getName().contains("$VALUES")) {
 93                valuesField = field;
 94                break;
 95            }
 96        }
 97        AccessibleObject.setAccessible(new Field[] { valuesField }, true);
 98  
 99        try {
100  
101            // 2. Copy it
102            T[] previousValues = (T[]) valuesField.get(enumType);
103            List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
104  
105            // 3. build new enum
106            T newValue = (T) makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues);
107  
108            // 4. add new value
109            values.add(newValue);
110  
111            // 5. Set new values field
112            setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
113  
114            // 6. Clean enum cache
115            cleanEnumCache(enumType);
116            return newValue;
117        } catch (Exception e) {
118            e.printStackTrace();
119            throw new RuntimeException(e.getMessage(), e);
120        }
121    }
122  
123  
124    public static void main(String[] args) {
125 
126    }
127    
128  
129 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM