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 }