直接上代码:
加入lombok依赖:
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.36version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.83version>
dependency>
dependencies>
Enum工具类:
import lombok.extern.slf4j.Slf4j;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.MethodAccessor;
import sun.reflect.ReflectionFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class EnumUtil {
/**
* 扩展枚举(把新枚举的值加入旧枚举里,从旧枚举里删除不要的枚举值)
* @param oldEnumClass 旧枚举类
* @param newEnumClass 需要扩展的枚举类
* @param removeOldEnums 需要删除的旧枚举值
* @param
* @param
* @throws Exception
*/
public static <O extends Enum,N extends Enum> void extendEnum(Class<O> oldEnumClass,Class<N> newEnumClass,O... removeOldEnums) throws Exception {
boolean needToRemoveOldEnum = removeOldEnums!=null&&removeOldEnums.length>0;
if(needToRemoveOldEnum){
removeEnum(oldEnumClass,removeOldEnums);
}
List newEnums = values(newEnumClass);
Field[] fields = getFields(newEnumClass);
for (Object newEnumObject : newEnums) {
Enum newEnum = Enum.class.cast(newEnumObject);
List<Class> fieldTypeList = new ArrayList<>();
//枚举名称的类型:String.class
fieldTypeList.add(String.class);
for (Field field : fields) {
fieldTypeList.add(field.getType());
}
List<Object> fieldValueList = new ArrayList<>();
//枚举名称
fieldValueList.add(newEnum.name());
for (Field field : fields) {
Object value = field.get(newEnum);
fieldValueList.add(value);
}
Class[] fieldTypes = fieldTypeList.toArray(new Class[]{});
String[] fieldValues = fieldValueList.toArray(new String[]{});
addEnum(oldEnumClass,fieldTypes,fieldValues);
}
}
/**
* 新增枚举值
* @param enumClass 枚举类型
* @param fieldTypes 字段的类型,第一个是枚举名类型String
* @param fieldValues 字段的值,第一个是枚举名称
* @throws Exception
*/
public static <T extends Enum<T>> T addEnum(Class<T> enumClass, Class[] fieldTypes, Object[] fieldValues) throws Exception {
if(fieldTypes==null||fieldTypes.length==0){
throw new RuntimeException("参数fieldTypes为空");
}
if(fieldValues==null||fieldValues.length==0){
throw new RuntimeException("参数fieldValues为空");
}
if(fieldTypes[0]!=String.class){
throw new RuntimeException("参数fieldTypes[0]不是String.class");
}
if(!(fieldValues[0] instanceof String)){
throw new RuntimeException("参数fieldValues[0]不是字符串");
}
if(fieldTypes.length!=fieldValues.length){
throw new RuntimeException("参数fieldTypes和参数fieldValues的长度不一致");
}
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
String enumName = fieldValues[0].toString();
synchronized (enumClass){
//判断name是否已经添加过了
if(hasEnumName(enumClass,enumName)){
log.info("枚举类{}中已存在该枚举名:{}",enumClass.getSimpleName(),enumName);
return getEnum(enumClass,enumName);
}
//name,ordinal,其他自定义字段
List<Class> allFieldClass = new ArrayList<>(fieldTypes.length + 1);
allFieldClass.add(String.class);
allFieldClass.add(int.class);
for (int i = 1; i < fieldTypes.length; i++) {
allFieldClass.add(fieldTypes[i]);
}
Class[] classes = allFieldClass.toArray(new Class[]{});
Constructor<T> constructor = enumClass.getDeclaredConstructor(classes);
ConstructorAccessor constructorAccessor = reflectionFactory.newConstructorAccessor(constructor);
List<Object> allFields = new ArrayList<>(fieldValues.length + 1);
allFields.add(enumName);
int maxOrdinal = getMaxOrdinal(enumClass);
allFields.add(maxOrdinal+1);
for (int i = 1; i < fieldValues.length; i++) {
allFields.add(fieldValues[i]);
}
//调用枚举的构造方法,创建新的枚举值
T newEnum = (T) constructorAccessor.newInstance(allFields.toArray());
log.info("新增枚举:{}" , newEnum);
Field valuesField = enumClass.getDeclaredField("$VALUES");
valuesField.setAccessible(true);
//解除values属性的final限制
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(valuesField);
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(valuesField, modifiers);
//将新增的枚举值加入values属性里
FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
T[] ts = (T[]) fieldAccessor.get(enumClass);
List<T> list = new ArrayList<>(Arrays.asList(ts));
list.add(newEnum);
fieldAccessor.set(enumClass, list.toArray(ts));
//将Class对象的enumConstants和enumConstantDirectory清空(Enum.valueOf()方法会给它们赋值)
/**
* Enum.valueOf()逻辑:
* 1.取enumConstantDirectory
* 1.1如果有值则直接返回
* 1.2如果没值,则取enumConstants,并拷贝到enumConstantDirectory,下次可直接返回
* 1.2.1如果enumConstants有值,则返回
* 1.2.2如果enumConstants没值,则取枚举的values属性,并拷贝到enumConstants,下次可直接返回
* 2.enumConstantDirectory.get(name)返回
*/
Field enumConstantDirectoryField = enumClass.getClass().getDeclaredField("enumConstantDirectory");
enumConstantDirectoryField.setAccessible(true);
FieldAccessor enumConstantDirectoryFieldAccessor = reflectionFactory.newFieldAccessor(enumConstantDirectoryField, false);
enumConstantDirectoryFieldAccessor.set(enumClass,null);
Field enumConstantsField = enumClass.getClass().getDeclaredField("enumConstants");
enumConstantsField.setAccessible(true);
FieldAccessor enumConstantsFieldAccessor = reflectionFactory.newFieldAccessor(enumConstantsField, false);
enumConstantsFieldAccessor.set(enumClass,null);
return newEnum;
}
}
/**
* 获取枚举类当前最大序号
* @param enumClass 枚举类型
* @param
* @return
* @throws Exception
*/
public static <T extends Enum<T>> int getMaxOrdinal(Class<T> enumClass) throws Exception {
List<T> values = values(enumClass);
if(values==null||values.size()==0){
return 0;
}
int maxOrdinal = 0;
for (T value : values) {
if(maxOrdinal<value.ordinal()){
maxOrdinal = value.ordinal();
}
}
return maxOrdinal;
}
/**
* 删除枚举值
* @param enumClass 枚举类型
* @param removeOldEnums 需要删除的枚举值
* @param
*/
public static <T extends Enum<T>> void removeEnum(Class<T> enumClass, Enum<T>... removeOldEnums) throws Exception {
if(removeOldEnums==null||removeOldEnums.length==0){
log.warn("removeOldEnums为空");
return;
}
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
synchronized (enumClass){
Field valuesField = enumClass.getDeclaredField("$VALUES");
valuesField.setAccessible(true);
//解除values属性的final限制
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(valuesField);
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(valuesField, modifiers);
FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
T[] oldEnumArray = (T[]) fieldAccessor.get(enumClass);
List<T> enumList = new ArrayList<>(Arrays.asList(oldEnumArray));
for (Enum removeOldEnum : removeOldEnums) {
//将指定的枚举值从values属性里删除
enumList.remove(removeOldEnum);
log.info("删除枚举值:{}",removeOldEnum);
}
//把List转成数组
T[] newEnumArray = (T[]) Arrays.copyOf(enumList.toArray(), enumList.size(), oldEnumArray.getClass());
fieldAccessor.set(enumClass, newEnumArray);
}
}
/**
* 修改枚举属性
* @param enumClass 枚举类型
* @param enumName 枚举名称
* @param attributeName 属性名
* @param attributeValue 属性值
* @param
* @throws Exception
*/
public static <T extends Enum<T>> void setAttribute(Class<T> enumClass,String enumName,String attributeName,Object attributeValue) throws Exception {
List<T> values = values(enumClass);
T target = null;
for (T t:values){
if(t.name().equals(enumName)){
target = t;
break;
}
}
if(target==null){
throw new RuntimeException("该枚举类没有枚举名:"+enumName);
}
Field declaredField = target.getClass().getDeclaredField(attributeName);
declaredField.setAccessible(true);
declaredField.set(target,attributeValue);
}
/**
* 修改枚举属性
* @param targetEnum 枚举值
* @param attributeName 属性名
* @param attributeValue 属性值
* @param
* @throws Exception
*/
public static <T extends Enum<T>> void setAttribute(T targetEnum,String attributeName,Object attributeValue) throws Exception {
Field declaredField = targetEnum.getClass().getDeclaredField(attributeName);
declaredField.setAccessible(true);
declaredField.set(targetEnum,attributeValue);
}
/**
* 判断枚举类是否包含了指定枚举名
* @param clazz
* @param enumName
* @param
* @return
*/
public static <T extends Enum<T>> boolean hasEnumName(Class<T> clazz, String enumName) throws Exception {
// 不要用valueOf方法,因为它会初始化enumConstantDirectory使得后续调用valueOf方法后拿不到后面加入的枚举值
// try{
// T t = Enum.valueOf(clazz, enumName);
// if(t!=null){
// return true;
// }
// }catch (Exception e){
// e.printStackTrace();
// System.err.println(e.getMessage());
// }
List<T> values = values(clazz);
for (T t:values){
if(t.name().equals(enumName)){
return true;
}
}
return false;
}
/**
* 根据枚举类型和枚举名获取枚举值
* @param clazz 枚举类型
* @param enumName 枚举名称
* @param
* @return
* @throws Exception
*/
public static <T extends Enum<T>> T getEnum(Class<T> clazz, String enumName) throws Exception {
List<T> values = values(clazz);
for (T t:values){
if(t.name().equals(enumName)){
return t;
}
}
return null;
}
/**
* 获取枚举类的所有枚举值
* @param clazz 枚举类型
* @param
* @return
* @throws Exception
*/
public static <T extends Enum<T>> List<T> values(Class<T> clazz) throws Exception {
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Field valuesField = clazz.getDeclaredField("$VALUES");
valuesField.setAccessible(true);
FieldAccessor fieldAccessor = reflectionFactory.newFieldAccessor(valuesField, false);
T[] ts = (T[]) fieldAccessor.get(clazz);
List<T> list = new ArrayList<>(Arrays.asList(ts));
return list;
}
/**
* 获取枚举类的所有枚举值
* @param clazz 枚举类型
* @param
* @return
* @throws Exception
*/
public static <T extends Enum<T>> List<T> values2(Class<T> clazz) throws Exception {
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Method valuesMethod = clazz.getDeclaredMethod("values");
valuesMethod.setAccessible(true);
MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(valuesMethod);
T[] ts = (T[]) methodAccessor.invoke(clazz, null);
List<T> list = new ArrayList<>(Arrays.asList(ts));
return list;
}
/**
* 获取枚举的字段(排除数组类型、枚举类型)
* @param enumClass
* @return
*/
public static Field[] getFields(Class<? extends Enum> enumClass){
List<Field> result = new ArrayList<>();
Field[] declaredFields = enumClass.getDeclaredFields();
for (Field field : declaredFields) {
Class<?> type = field.getType();
//排除数组类型(values)、枚举类型(枚举值)
if(type!=enumClass && !type.isArray()){
field.setAccessible(true);
result.add(field);
}
}
return result.toArray(new Field[]{});
}
/**
* 打印枚举值
* @param enumClass 枚举类型
* @throws Exception
*/
public static void printEnum(Class<? extends Enum> enumClass) throws Exception {
System.out.println("+++++++++++++++++++++++");
List values = values(enumClass);
values.stream().forEach(System.out::println);
System.out.println("+++++++++++++++++++++++");
}
}
有个第三方jar包的User类,User类里有个属性gender,类型是枚举GenderEnum:
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@SuperBuilder
@Data
public class User {
private String username;
private GenderEnum gender;
}
public enum GenderEnum {
MAN("man","男"),
WOMEN("women","女"),
RENYAO("renyao","人妖"),
;
private String key;
private String displayName;
GenderEnum(String key, String displayName){
this.key = key;
this.displayName = displayName;
System.out.println("调用了GenderEnum枚举构造器创建枚举值:"+name());
}
@Override
public String toString() {
return "GenderEnum{" +
"ordinal="+ordinal()+
", name='"+name()+"'"+
", key='" + key + "'"+
", displayName='" + displayName + "'" +
"}";
}
}
现在项目需要使用User类,但是想去掉“人妖”这个枚举,改成“其他”(无法修改第三方jar包的源码)。
先定义新的枚举类EnhanceGenderEnum:
public enum EnhanceGenderEnum {
MAN("man","男"),
WOMEN("women","女"),
OTHER("other","其他"),
;
private String key;
private String displayName;
EnhanceGenderEnum(String key, String displayName){
this.key = key;
this.displayName = displayName;
System.out.println("调用了EnhanceGenderEnum枚举构造器创建枚举值:"+name());
}
@Override
public String toString() {
return "EnhanceGenderEnum{" +
"ordinal="+ordinal()+
", name='"+name()+"'"+
", key='" + key + "'"+
", displayName='" + displayName + "'" +
"}";
}
}
如此一来,即可对GenderEnum进行增强了(见test()方法):
import com.alibaba.fastjson.JSON;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
public class GenderEnumTest {
@Test
public void printEnumTest() throws Exception{
EnumUtil.printEnum(GenderEnum.class);
EnumUtil.printEnum(EnhanceGenderEnum.class);
}
@Test
public void addEnumTest() throws Exception{
EnumUtil.printEnum(GenderEnum.class);
GenderEnum addedEnum = EnumUtil.addEnum(GenderEnum.class, new Class[]{String.class,String.class,String.class}, new Object[]{"OTHER","other","其他"});
EnumUtil.printEnum(GenderEnum.class);
boolean addSuccess = false;
for (GenderEnum value : GenderEnum.values()) {
if(value==addedEnum){
addSuccess = true;
}
}
Assert.assertTrue(addSuccess);
GenderEnum other = GenderEnum.valueOf("OTHER");
Assert.assertTrue(other!=null&&other==addedEnum);
}
@Test
public void removeTest() throws Exception{
EnumUtil.printEnum(GenderEnum.class);
EnumUtil.removeEnum(GenderEnum.class,GenderEnum.RENYAO);
EnumUtil.printEnum(GenderEnum.class);
boolean removeSuccess = true;
for (GenderEnum value : GenderEnum.values()) {
if(value==GenderEnum.RENYAO){
removeSuccess = false;
}
}
Assert.assertTrue(removeSuccess);
}
@Test
public void hasEnumNameTest() throws Exception {
boolean hasEnumName = EnumUtil.hasEnumName(GenderEnum.class, "RENYAO");
Assert.assertTrue(hasEnumName);
}
@Test
public void getEnumTest() throws Exception {
GenderEnum anEnum = EnumUtil.getEnum(GenderEnum.class, "RENYAO");
Assert.assertTrue(anEnum!=null);
}
@Test
public void getFieldsTest(){
Field[] fields = EnumUtil.getFields(GenderEnum.class);
for (Field field : fields) {
System.out.println(field);
}
}
@Test
public void extendEnumTest() throws Exception{
EnumUtil.printEnum(GenderEnum.class);
EnumUtil.extendEnum(GenderEnum.class,EnhanceGenderEnum.class,GenderEnum.RENYAO);
EnumUtil.printEnum(GenderEnum.class);
}
@Test
public void setAttributeTest1() throws Exception {
System.out.println(GenderEnum.RENYAO);
EnumUtil.setAttribute(GenderEnum.class,GenderEnum.RENYAO.name(),"displayName","其他");
System.out.println(GenderEnum.RENYAO);
EnumUtil.printEnum(GenderEnum.class);
}
@Test
public void setAttributeTest2() throws Exception {
System.out.println(GenderEnum.RENYAO);
EnumUtil.setAttribute(GenderEnum.RENYAO,"displayName","其他");
System.out.println(GenderEnum.RENYAO);
EnumUtil.printEnum(GenderEnum.class);
}
@Test
public void test() throws Exception{
EnumUtil.extendEnum(GenderEnum.class,EnhanceGenderEnum.class,GenderEnum.RENYAO);
User user = User.builder().username("sky").gender(GenderEnum.valueOf(EnhanceGenderEnum.OTHER.name())).build();
String json = JSON.toJSONString(user);
System.out.println(json);
User user1 = JSON.parseObject(json, User.class);
System.out.println(user1.getGender());
}
}