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 }

 

posted @ 2020-04-10 10:50  link_ed  阅读(16237)  评论(1编辑  收藏  举报