java推荐使用枚举定义常量

本文探讨了枚举常量在程序开发中的应用优势,包括增强代码的可读性和可维护性,提供内置方法及自定义方法的能力,并通过具体示例说明了枚举常量相较于传统常量的优越性。


假如有一笔业务需要审核,审核状态分:未审核,审核中,审核通过,审核不通过。我们在程序里是否可以直接这么写:

if(state==1){//1代表未操作

      //操作

}else{

     //......

}

将状态标识直接写在代码里面(硬编码),只图一时方便,却是后患无穷,如果有一天你需要修改状态标识,用0代表未审核而不是1,你不得不将所有与该标识相关的代码都找出来一个个改,另外,在编码过程中,标识输入错误的概率是比较高的,一不小心把0输入成了10,虽然不会提示任何编译错误,但运行结果将是出乎人的意料的。

于是我们很快想到可以用常量代替:

public static final int UNAUDIT = 0;

相关判断代码则是:

if(state==CONSTANT.UNAUDIT){

      //操作

}else{

      //......

}

这段代码比硬编码更加健壮容易维护,但是仍然有不足之处。

1、UNAUDIT是编译期常量,如果其值被改变,那么使用方需要重新编译。

2、没有简便的方法获取标识代表的字符串描述。

于是我们用枚举类来代替常量。

public enum AuditState {

     UNAUDIT(1),

     AUDITING(2),

     AUDIT_SUCCESS(3),

     AUDIT_FAIL(4);

private final int statenum;

    AuditState(int statenum){

   this.statenum = statenum;

}

public int getStatenum() {

    return statenum;

}

}

调用如下:

if (state == AuditState.UNAUDIT.getStatenum()) {

    //AuditState.UNAUDIT.toString()获取字符串描述

System.out.println(

     AuditState.UNAUDIT.toString() + "标识是 "

    + AuditState.UNAUDIT.getStatenum());

} else {

   //......

}

枚举类还有更加强大的功能,如添加字段,方法,还可以对他进行遍历访问


一、分析 

常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量。不过,在1.5版之后有了改进,即新增了一种常量声明方式,枚举常量。代码如下: 

enum Season{ 
    Spring,Summer,Autumn,Winter; 
} 

二、场景 

那么枚举常量与我们的经常使用的类常量和静态常量比有什么优势呢 

1.枚举常量更简单 

先把Season枚举翻译成接口,代码如下 

interface Season{ 
    int Sprint = 0; 
    int Summer = 1; 
    int Autumn = 2; 
    int Winter = 3; 
} 

枚举只需要定义每个枚举项,不需要定义枚举值,而接口常量(或类常量)则必须定义值,否则编译通不过;两个引用的方式相同(都是“类名.属性”,如Season.Sprint),但是枚举表示的是一个枚举项,字面含义是春天,而接口常量却是一个Int类型 

2.枚举常量属于稳态型 

使用常量接口,我们得对输入值进行检查,确定是否越界,如果常量非常庞大,校验输入就是一件非常麻烦的事情,但这是一个不可逃避的过程。 

public void describe(int s){ 
    //s变量不能超越边界,校验条件 
    if(s >= 0 && s <4){ 
        switch(s){ 
            case Season.Summer: 
                System.out.println("Summer is very hot!"); 
                break; 
            case Season.Winter: 
                System.out.println("Winter is very cold!"); 
                break; 
        ….. 
        } 
    } 
} 

我们再来看看枚举常量是否能够避免校验问题,代码如下 

public void describe(Season s){ 
    switch(s){ 
        case Season.Summer: 
            System.out.println("Summer is very hot!"); 
            break; 
        case Season.Winter: 
            System.out.println("Winter is very cold!"); 
            break; 
        …... 
    } 
} 

不用校验,已经限定了是Season枚举,所以只能是Season类的四个实例。这也是我们看重枚举的地方:在编译期间限定类型,不允许发生越界的情况。 

3.枚举具有内置方法 

每个枚举都是java.lang.Enum的子类,该基类提供了诸如获得排序值的ordinal方法、compareTo比较方法等,大大简化了常量的访问。比如,列出所有枚举值: 

public static void main(String[] args){ 
    for(Season s:Season.values()){ 
        System.out.println(s); 
    } 
} 

4.枚举可以自定义方法 

这一点似乎不是枚举的优点,类常量也可以有自己的方法,但关键是枚举常量不仅仅可以定义静态方法,还可以定义非静态方法,而且还能够从根本上杜绝常量类被实例化。比如我们在定义获取最舒服的季节,使用枚举的代码如下: 

enum Season{ 
    Spring,Summer,Autumn,Winter; 
    //最舒服的季节 
    public static Season getComfortableSeason(){ 
        return Spring; 
    } 
} 

那如果是使用类常量如何实现呢?如下 

class Season{ 
    public final static int Spring = 0; 
    public final static int Summer = 1; 
    public final static int Autumn = 2; 
    public final static int Winter = 3; 
 
    //最舒服的季节 
    public static int getComfortableSeason(){ 
        return Spring; 
    } 
} 

虽然枚举在很多方面都比接口常量和类常量好用,但是它有一点比不上接口常量和类常量的,就是继承,枚举类型是不能有继承的,也就是说一个枚举常量定义完毕后,除非修改重构,否则无法做扩展。 

三、建议 

在项目开发中,推荐使用枚举常量代替接口常量或类常量 

<think> 我们正在讨论Java枚举在Android开发中转换为常量的方法。根据用户的问题,他提到在Android开发中需要将枚举转换为常量,可能是为了在Intent之间传递或者存储到SharedPreferences等场景,因为枚举对象本身不能直接传递,而需要转换为基本数据类型(如int或String)。 引用[2]中提到:用户有一个枚举类型ReportTypeEnum,通过ordinal()方法获取int(即枚举的序号),然后在另一个页面(JSP)中需要将这个int转换回枚举。这实际上就是枚举和int之间的转换方法。 但是需要注意的是,使用ordinal()方法依赖于枚举常量的声明顺序,如果枚举声明顺序改变,ordinal也会改变,这可能导致之前存储的ordinal失效。因此,更安全的做法是自定义一个int,而不是依赖ordinal。 在Android开发中,我们通常使用以下两种方法将枚举转换为常量: 方法一:使用ordinal()和values()数组 - 将枚举转换为int:调用枚举实例的ordinal()方法,得到它在枚举声明中的位置(从0开始)。 - 将int转换回枚举:通过枚举类的values()方法获取所有枚举常量组成的数组,然后通过下标(即ordinal)取出对应的枚举常量。 但是,这种方法有上述提到的顺序依赖问题。 方法二:自定义int - 在枚举定义一个字段来存储自定义的int(可以是任何整数,不要求连续,也不要求从0开始)。 - 在构造方法中初始化这个字段。 - 提供get方法获取这个int。 - 编写一个静态方法,通过自定义int查找对应的枚举常量(遍历values(),比较自定义字段)。 方法二更安全,因为自定义的int是固定的,不会随着枚举声明顺序的改变而改变。 下面分别给出两种方法的示例: 方法一示例(使用ordinal): ```java public enum ReportType { TYPE_A, TYPE_B, TYPE_C; // 将枚举转换为int(ordinal) public int toInt() { return this.ordinal(); } // 将int转换回枚举 public static ReportType fromInt(int value) { // 注意:需要确保value在有效范围内 if (value < 0 || value >= values().length) { throw new IllegalArgumentException("Invalid ordinal for ReportType: " + value); } return values()[value]; } } ``` 方法二示例(自定义int): ```java public enum ReportType { TYPE_A(10), TYPE_B(20), TYPE_C(30); private final int value; ReportType(int value) { this.value = value; } public int getValue() { return value; } // 通过自定义获取枚举 public static ReportType fromValue(int value) { for (ReportType type : values()) { if (type.value == value) { return type; } } throw new IllegalArgumentException("Invalid value for ReportType: " + value); } } ``` 在Android中,我们通常使用Parcelable传递对象,但是枚举默认不是Parcelable。不过,我们可以将枚举转换为int(通过上述方法)然后放入Intent中,在接收方再转换回来。 例如,在ActivityA中: ```java int reportValue = ReportType.TYPE_A.getValue(); // 或者toInt(),取决于你用的方法 intent.putExtra("REPORT_TYPE", reportValue); ``` 在ActivityB中: ```java int reportValue = getIntent().getIntExtra("REPORT_TYPE", -1); if (reportValue != -1) { ReportType reportType = ReportType.fromValue(reportValue); // 或者fromInt // 使用reportType } ``` 注意事项: 1. 如果使用ordinal(),要确保枚举常量的顺序不会改变(包括新增枚举常量时不要插入中间,否则序号会变)。 2. 自定义int的方法更加灵活且稳定,推荐使用。 另外,引用[1]提到枚举比静态常量占用更多内存。在Android开发中,如果对内存非常敏感(例如有大量枚举对象使用),可以考虑使用常量类(如public static final int)代替枚举。但在大多数情况下,枚举的可读性和类型安全性带来的好处更重要。 因此,在Android开发中,如果需要在组件之间传递枚举,将其转换为int(或String,同样可以实现自定义字符串表示)是常用的方法。 相关问题: 1. 在Android中如何通过枚举实现状态机? 2. 枚举常量类在性能上有何差异?在Android中如何选择? 3. 如何将枚举转换为字符串,以便在Intent中传递? 4. 使用枚举作为SharedPreferences的键类型时需要注意什么? </think> ### Java安卓开发中枚举转换为常量的方法 在Android开发中,将枚举转换为常量主要有以下两种方法,兼顾**安全性**和**性能优化**: --- #### 方法一:使用`ordinal()`方法(简单但不推荐) 直接获取枚举常量的声明序号(从0开始): ```java public enum ReportType { PDF, EXCEL, HTML; // 枚举声明顺序 // 转换方法 public int toInt() { return this.ordinal(); } } // 使用示例 int reportCode = ReportType.PDF.toInt(); // 返回0 ``` **缺点**: 1. **耦合度高**:若调整枚举声明顺序(如新增枚举),序号会变化导致数据错乱[^2] 2. **可读性差**:数无业务含义(如0不代表PDF) --- #### 方法二:自定义字段(推荐方案) 为每个枚举常量指定固定数,避免序号依赖: ```java public enum ReportType { PDF(1001), EXCEL(2001), HTML(3001); // 自定义唯一ID private final int id; // 常量字段 ReportType(int id) { this.id = id; } // 枚举 → 数 public int toInt() { return id; } // 数枚举(含容错) public static ReportType fromInt(int id) { for (ReportType type : values()) { if (type.id == id) return type; } throw new IllegalArgumentException("无效报告类型ID: " + id); } } // 使用示例 // 1. 枚举转数 int reportCode = ReportType.PDF.toInt(); // 返回1001 // 2. 数枚举(如从Intent获取) ReportType type = ReportType.fromInt(intent.getIntExtra("REPORT_TYPE", 0)); ``` **优势**: 1. **数据稳定**:自定义ID与声明顺序解耦[^2] 2. **业务语义**:ID可体现业务含义(如1001=PDF) 3. **类型安全**:`fromInt()`方法验证有效性 --- ### Android开发中的优化建议 1. **Parcelable传递优化** 若需跨进程传递枚举,优先序列化为数: ```java // 在ActivityA中 intent.putExtra("REPORT_TYPE", ReportType.PDF.toInt()); // 在ActivityB中 int code = getIntent().getIntExtra("REPORT_TYPE", -1); ReportType type = ReportType.fromInt(code); ``` 2. **内存敏感场景** 在低内存设备或高频调用场景(如RecyclerView适配器),考虑用`@IntDef`替代枚举: ```java @IntDef({REPORT_PDF, REPORT_EXCEL, REPORT_HTML}) public @interface ReportType {} public static final int REPORT_PDF = 1001; public static final int REPORT_EXCEL = 2001; public static final int REPORT_HTML = 3001; ``` 兼顾类型安全和性能(本质是整型常量)[^1] 3. **JSON序列化** 使用Gson时可自定义序列化规则: ```java public class ReportTypeAdapter implements JsonSerializer<ReportType> { @Override public JsonElement serialize(ReportType src, Type type, JsonSerializationContext context) { return new JsonPrimitive(src.toInt()); // 输出数而非对象名 } } ``` --- ### 关键注意事项 1. **版本兼容** - 自定义ID一旦发布**禁止修改**(否则新旧版本数据不兼容) - 新增枚举时需确保ID全局唯一 2. **性能对比** | 方案 | 内存占用 | 执行效率 | 安全性 | |---------------------|----------|----------|--------| | `ordinal()` | 低 | 高 | 差 | | 自定义字段 | 中 | 中 | 高 | | `@IntDef`+常量 | **最低** | **最高** | 中 | 3. **防御式编程** `fromInt()`中务必添加无效检查: ```java // 避免使用 !typeMap.contains(id) 的无效状态 throw new IllegalArgumentException("Invalid ID: " + id); ``` --- > 💡 **决策建议**: > - 需要强类型检查 → 用**自定义字段枚举** > - 内存敏感场景 → 用`@IntDef`+整型常量[^1][^3] > - 跨进程通信 → 优先序列化为数 --- ### 相关问题 1. 如何通过`@IntDef`在Android中实现类型安全的常量? 2. 枚举在Android序列化(Parcelable)时需要注意什么? 3. 为什么Android官方推荐用`@IntDef`替代部分枚举场景? 4. 如何为枚举定义toString()方法实现友好显示? 5. 枚举在Room数据库中的存储和查询最佳实践是什么? [^1]: 枚举实现原理及内存占用分析 [^2]: 枚举序数与自定义转换的对比 [^3]: Android平台枚举使用准则
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值