• Java中如何根据值获取泛型枚举对象及EnumUtil的必要性


    一、抽取EnumUtil的必要性

    比如说,我在业务中定义了一个表示“加密类型”的枚举类 EncryptType

    import cn.hutool.core.util.StrUtil;
    import com.suning.tech.exception.GatewayRuntimeException;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    import java.util.Arrays;
    import java.util.Optional;
    
    /**
     * 功能描述:加密类型
     */
    @AllArgsConstructor
    @Getter
    public enum EncryptType {
    
        NONE (0), // 不加密
        AES(1), // 1:aes
        RSA(2), // 2:rsa
        XXTEA(3); // 3:xxtea
    
        int type;
    
        public static EncryptType parseInt(int typeVal) {
            Optional<EncryptType> result = Arrays.stream(values()) // values() 可以获取当前枚举类所有枚举常量
                .filter(t -> t.getType() == typeVal) // 判断相等的条件
                .findFirst();
            if (result.isPresent()) {
                return result.get();
            } else {
                throw new GatewayRuntimeException(StrUtil.format("No EncryptType matches type value {}", typeVal)); // NOSONAR
            }
        }
    }
    

    这样做的好处是

    1. 避免客户端代码中的魔法值;
    2. 客户端代码更加清晰明了;
    EncryptType encryptType = EncryptType.parseInt(type);
    if (EncryptType.XXTEA.equals(encryptType)) {
      // XXTEA 加密
    } else if (EncryptType.AES.equals(encryptType)) {
      // AES 加密
    } else if (EncryptType.RSA.equals(encryptType)) {
      // RSA 加密
    }
    

    你可以想象一下,如果直接拿 int 类型的type 和 1,2,3 做比较,代码看起来会有多糟糕。

    但是,枚举类型使用得多了以后,需要在每一个枚举类中,都写一段 parseInt 类型的代码,那得多糟心!

    比如,我的项目中,就有这么多枚举类型,都需要增加解析方法!

    这不够简洁啊!!! 于是,我尝试用泛型来抽取这段方法。

    二、我的方案

    import cn.hutool.core.util.StrUtil;
    import org.springframework.util.ReflectionUtils;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.Optional;
    
    /**
     * 功能描述: 枚举工具类
     *
     * @author geekziyu
     * @version 1.0.0
     */
    public class EnumUtil {
    
        public static <E extends Enum<E>> E valueOf(Class<E> e, String typeValue) {
            E[] values = e.getEnumConstants();
            Optional<E> result = Arrays.stream(values)
                    .filter(t -> String.valueOf(getTypeValue(t)).equals(typeValue)) // 用字符串形式比较
                    .findFirst();
            if (result.isPresent()) {
                return result.get();
            } else {
                throw new RuntimeException(StrUtil.format("No {} matches type value {}", e.getTypeName(), typeValue)); // NOSONAR
            }
        }
    
        public static <E extends Enum<E>> E valueOf(Class<E> e, int typeValue) {
            return valueOf(e, String.valueOf(typeValue));
        }
    
    
        private static <E extends Enum<E>> Object getTypeValue(E e) {
            Class<? extends Enum> typeClass = e.getClass();
            try {
                Method getter = typeClass.getMethod("getTypeValue");
                ReflectionUtils.makeAccessible(getter);
                return getter.invoke(e);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException(StrUtil.format("No such method named 'getTypeValue' in Enum {}!", typeClass.getTypeName())); // NOSONAR
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(StrUtil.format("Inaccessible is the method 'getTypeValue' in Enum {} !", typeClass.getTypeName()), ex.getCause()); // NOSONAR
            } catch (InvocationTargetException ex) {
                throw new RuntimeException(StrUtil.format("Exception occurred to method 'getTypeValue' in  Enum {}!", typeClass.getTypeName()), ex.getCause()); // NOSONAR
            }
        }
    }
    

    当然,我这个工具类也有局限性,那就是我限定了枚举类中,必须要有一个名为 typeValue 的字段且必须要有 getTypeValue 方法!

    三、hutool的解决方案

    public static <E extends Enum<E>> E likeValueOf(Class<E> enumClass, Object value) {
      if (value instanceof CharSequence) {
        value = value.toString().trim();
      }
    
      final Field[] fields = ReflectUtil.getFields(enumClass);
      final Enum<?>[] enums = enumClass.getEnumConstants();
      String fieldName;
      for (Field field : fields) {
        fieldName = field.getName();
        if (field.getType().isEnum() || "ENUM$VALUES".equals(fieldName) || "ordinal".equals(fieldName)) {
          // 跳过一些特殊字段
          continue;
        }
        for (Enum<?> enumObj : enums) {
          if (ObjectUtil.equal(value, ReflectUtil.getFieldValue(enumObj, field))) {
            return (E) enumObj;
          }
        }
      }
      return null;
    }
    

    EncryptType 为例,有以下字段:

    字段 field.getType().isEnum() 类型 fieldName
    EncryptType.NONE true EncryptType "NONE"
    EncryptType.AES true EncryptType "AES"
    EncryptType.RSA true EncryptType "RSA"
    EncryptType.XXTEA true EncryptType "XXTEA"
    EncryptType.type false int "type"
    EncryptType.$VALUES false EncryptType[] "$VALUES"
    EncryptType.name false String "name"
    EncryptType.ordinal false int "ordinal"

    ObjectUtil.equal 的最普遍的“相等”判断依据是 (a == b) || (a != null && a.equals(b))

  • 相关阅读:
    C# 规格说明书
    C#学习笔记----复习笔记
    C#学习笔记-stream,win8.1开发小记
    C#学习笔记-Win8.1 store app winform开发小记
    C#学习笔记:linq和xml
    C#第六周--关于正则表达式应用,delegates关键字
    C#第六课---struct,interface
    C#第五课--继承和多态
    开发了一款小程序
    「CSS Warning 2」icon 的做法
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/15993179.html
Copyright © 2020-2023  润新知