通用的任意值转Java枚举常量工具类

Java 的枚举类型,自带一个valueOf() 方法,来转换字符串到对应的枚举常量,同时,也可以利用枚举类型的 values()[x] 按顺序去取相应的常量。但是对于有自定义数值的枚举,却需要自己处理。例如:

    public enum NamedColor {
        Red(0xFF0000,"红色"),
        Green(0x00FF00,"绿色"),
        Blue(0x0000FF,"蓝色");


        private int value;
        private String name;
        NamedColor(int value,String name){
            this.value=value;
            this.name = name;
        }

        public int getValue()
        {
            return this.value;
        }
        public String getName()
        {
            return  this.name;
        }

    }

若要把 0xFF0000,或者“红色”转成 NamedColor.Red, 可能需要专门针对这个枚举类写个方法了。

有没有通用的方法呢?本人受 MyBatis Plus的 EnumUtil工具类的启发,写了如下代码,利用Lamda表达式,可以将任意值和任意指定的枚举类型匹配。代码如下:

先声明一个IGetter的函数接口:

  @FunctionalInterface
    public interface IGetter<T> extends Serializable {
        Object apply(T source);
    }

然后如下处理:

public static  <E extends Enum> E valueOfEnum(@NotNull Class<E> enumClass, @NotNull Object value, @NotNull IGetter<E> ... vGetters )
    {
        // Step 1. 使用Java 自带的valueOf判断是否匹配枚举常量名
        try{
            E ec = (E) Enum.valueOf(enumClass,String.valueOf(value)) ;
            return ec;
        }catch (IllegalArgumentException e){
            log.debug(e.getMessage()+",Try to match with value and getters ...");
        }
        //Step 2 用指定的getter方法,逐个匹配枚举常量的属性值
        E[] enumConsts  = (E[])enumClass.getEnumConstants();
        // Step 2.1  确保以字符串类型给出的数字类型属性值value,也能匹配
        BigDecimal nv = null;
        try {
            nv = new BigDecimal(String.valueOf(value));
        }catch( Exception  ex ){
            log.debug(" Value:'{}' is NOT a valid number.{}",value,ex.getMessage() );
        }
        //Step 2.2
        for ( IGetter<E> getter:vGetters) {
            for (E e:enumConsts){
                Object ev = getter.apply(e);
                if (ev instanceof Number  && nv !=null && nv.compareTo(new BigDecimal(String.valueOf(ev))) == 0) {
                    return e;
                }
                if (Objects.equals(ev, value)) {
                    return e;
                }
            }
        }

        return null;
    }

调用方法:

NamedColor color1 = EnumUtils.valueOfEnum(NamedColor.class,"红色", NamedColor::getName);
NamedColor color2 = EnumUtils.valueOfEnum(NamedColor.class,0xFF00, NamedColor::getValue);

若不知道要匹配的值是什么类型,也可以把可能的getter都放进去:

NamedColor color3 = EnumUtils.valueOfEnum(NamedColor.class,obj, NamedColor::getName,NamedColor::getValue);
        

 

<think>我们被要求对比枚举类和常量类在消除魔法时的适用场景。根据引用内容,我们可以总结如下:1.**枚举类(Enum)**:适用于表示一组固定的常量集合,且这些常量不仅仅是简单的,可能还需要承载行为或更复杂的结构(引用[1])。枚举提供类型安全,可以避免无效(引用[5]),并且能够通过枚举的方法(如values(),ordinal(),name())提供便利(引用[2])。枚举类还可以消除判断语句,提高代码可维护性(引用[2])。2.**常量类(使用publicstaticfinal字段)**:适用于定义简单的、独立的常量。当常量之间没有逻辑关联,或者只需要一个简单的(如最大重试次数、超时时间等)时,使用常量类更简洁。但常量类无法保证类型安全,例如在引用[5]中,使用常量类定义的星期常量,在调用方法时可能传入不在常量集范围内的(如5),而编译器不会报错。###适用场景对比|**特性**|**枚举类**|**常量类**||------------------------|-----------------------------------------------|-----------------------------------------------||**适用场景**|一组有限的、预定义的常量(如状态码、类型等)|独立的、分散的常量(如配置参数、数学常数)||**类型安全**|是(编译期检查)|否(可能传入非法)[^5]||**可扩展性**|支持添加方法、字段和实现接口[^1]|仅支持常量,无法添加行为||**关联行为**|可在枚举中定义方法实现行为封装[^1]|无法直接关联行为,需借助外部工具类||**可读性**|高(枚举具有语义化名称)|中(依赖命名规范)||**维护性**|高(集中管理,修改枚举定义即可)|中(需查找替换,可能遗漏)||**序列化/反序列化**|内置支持(name()和valueOf())[^2]|需手动实现转换逻辑||**多关联**|支持(如一个枚举可关联code和desc)[^1][^3]|需定义多个常量并手动关联||**集合操作**|支持(values()返回所有枚举)[^2]|需手动维护集合|###具体场景示例####**场景1:订单状态(适合枚举)**```java//枚举实现:状态+行为publicenumOrderStatus{CREATED(1,"已创建"){@OverridepublicbooleancanCancel(){returntrue;}},PAID(2,"已支付"){@OverridepublicbooleancanCancel(){returnfalse;}};privatefinalintcode;privatefinalStringdesc;OrderStatus(intcode,Stringdesc){this.code=code;this.desc=desc;}publicabstractbooleancanCancel();//抽象方法定义状态行为}```**优势**:将状态码、描述和行为封装在一起,避免状态判断分散在各处[^1][^3]。---####**场景2:系统配置参数(适合常量类)**```java//常量类实现publicclassSystemConfig{publicstaticfinalintMAX_LOGIN_ATTEMPTS=5;publicstaticfinalStringDEFAULT_TIMEZONE="UTC+8";}```**原因**:这些常量是独立的数/字符串,无关联行为,也不需要类型检查。---####**场景3:错误码映射(适合枚举)**```javapublicenumErrorCode{SUCCESS(0,"成功"),PARAM_ERROR(1001,"参数错误");privatefinalintcode;privatefinalStringmessage;//构造方法及getterpublicstaticErrorCodefromCode(intcode){//查找逻辑}}```**优势**:集中管理错误码和描述,并提供code→枚举转换[^1]。---####**场景4:数学常数(适合常量类)**```javapublicclassMathConstants{publicstaticfinaldoublePI=3.1415926535;publicstaticfinaldoubleGOLDEN_RATIO=1.6180339887;}```**原因**:简单数常量,无行为关联。---###总结选择原则1.**优先枚举**:当常量满足以下任一条件时:-属于同一逻辑范畴(如状态、类型等)-需要关联额外数据(如描述字段)-需要定义基于常量的行为方法-需要确保类型安全(避免非法传参)[^5]2.**使用常量类**:当常量:-是全局配置参数(如超时时间)-是数学或物理常数-被多处使用但无逻辑关联>根据阿里巴巴Java开发手册,*状态码、类型码等必须使用枚举*[^1][^2]。而类似缓存前缀等字符串常量,则适合用常量类定义。§§相关问题§§1.枚举类中如何优雅地实现状态机转换?2.常量类在分布式配置场景下的局限性是什么?3.如何通过注解处理器验证魔法的使用?4.枚举类与常量类在序列化性能上有何差异?</think>### 枚举类与常量类在消除魔法中的适用场景对比 在 Java 开发中,枚举类和常量类都是消除魔法的重要手段,但它们的适用场景有显著差异: --- #### **1. 常量类(`public static final`)适用场景** ```java public class SystemConstants { // 简单独立常量 public static final int MAX_RETRY_COUNT = 3; public static final String DEFAULT_TIMEZONE = "UTC+8"; // 技术性配置参数 public static final int CACHE_EXPIRE_SECONDS = 3600; } ``` **适用场景特点**: - **简单独立**:无逻辑关联的离散(如数学常数、技术参数) - **纯数/字符串**:不需要附加行为或描述信息 - **跨模块共享**:被多个不相关类使用的全局常量 - **基础配置参数**:如超时时间、重试次数等[^1] > ✅ **典型用例**:连接超时时间、缓存有效期、数学常数(PI)等 --- #### **2. 枚举类(`enum`)适用场景** ```java public enum OrderStatus { // 带状态码和行为的枚举 CREATED(1, "已创建") { @Override public boolean canCancel() { return true; } }, PAID(2, "已支付") { @Override public boolean canCancel() { return false; } }; private final int code; private final String desc; OrderStatus(int code, String desc) { this.code = code; this.desc = desc; } public abstract boolean canCancel(); // 抽象方法定义行为 } ``` **适用场景特点**: - **有限状态集合**:固定数量的互斥状态(如订单状态、星期枚举) - **需要关联数据**:每个需要附加描述、状态码等元数据[^2] - **类型安全要求高**:需防止无效传入(如 `getToday(5)` 会报错)[^5] - **需要封装行为**:不同状态需要不同业务逻辑(如状态流规则) - **需要映射关系**:状态码 ↔ 描述文本的双向转换[^4] > ✅ **典型用例**:订单状态、错误码体系、业务类型等 --- ### **核心差异对比表** | 特性 | 常量类 | 枚举类 | |---------------------|--------------------------------|--------------------------------| | **本质** | 静态字段集合 | 特殊类(继承`java.lang.Enum`) | | **类型安全** | ❌ 可传入任意int/String [^5] | ✅ 编译期检查 | | **关联数据能力** | ❌ 只能存储单一 | ✅ 支持字段+方法封装 [^1][^2] | | **行为封装** | ❌ 无法关联行为 | ✅ 可为每个实现特定方法 | | **集合操作** | ❌ 需手动维护集合 | ✅ 内置`values()`遍历全部 | | **序列化/反序列化** | ❌ 需自定义逻辑 | ✅ 内置`name()`/`valueOf()` | | **适用规模** | 任意数量 | 有限集合(通常 < 50个) | --- ### **决策树:如何选择?** ```mermaid graph TD A[需要消除魔法] --> B{是否有业务含义?} B -->|是| C{是否属于有限状态集?} C -->|是| D[使用枚举类] C -->|否| E[使用常量类] B -->|纯技术参数| E D --> F{需要关联行为?} F -->|是| G[为枚举添加抽象方法] F -->|否| H[基础枚举+字段] ``` > 根据阿里巴巴开发规范:**业务状态码必须使用枚举**,技术参数可使用常量类[^1][^2] --- ### **混合使用案例** ```java // 常量类定义配置 public class CacheConfig { public static final String PREFIX = "ORDER_"; } // 枚举类定义状态 public enum CacheType { ORDER_DETAIL(CacheConfig.PREFIX + "DETAIL"), ORDER_LIST(CacheConfig.PREFIX + "LIST"); private final String key; CacheType(String key) { this.key = key; } } ``` > 💡 当枚举需要引用技术常量时,可组合使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值