zoukankan      html  css  js  c++  java
  • Java 枚举详解

    为什么要用枚举

    在博客系统中,一篇文章有且可能有这几种状态, 数据库中article文章表中state字段存储数值,表示其状态:

    • 0(已发表Published)
    • 1(草稿Draft)
    • 2(撤回撤回(Delete)

    文章实体类中用整数类型的state实例变量标识状态:

    public class Article {
        /* 文章状态可能值:0,1,2 */
        private int state;
        ...
    }

    Service层调用DAO层修改文章状态为‘已发表’:

    /**
     * dao接口修改文章状态方法
     * @param articleId 文章ID
     * @param state 状态
     */
    int updateState(int articleId, int state);
    
    
    // `Service`层修改文章状态的调用Dao代码:
    articleDao.updateState(id, 0);

    以上代码有两个问题:

    1. state参数传递并没有限定范围(0,1,2);
    2. 传递数据参数的代码,缺少语义,不看文档或注释不知道0是什么含义;

    先来解决第二个问题, 在JDK1.5前常用的解决方式:

    /**
     * 定义了文章的状态
     */
    public interface ArticleState{
        // 发布状态
        int PUBLISHED = 0;
        // 草稿状态
        int DRAFT = 1;
        // 撤回状态
        int DELETE = 2;
    }

    此时修改文章状态的代码:

    articleDao.updateState(id, ArticleState.PUBLISHED);

    然而此处没有限制必须通过ArticleState传递参数,JDK1.5后提供了枚举来解决这类问题。

    Java中声明

    在java中,使用enum关键字声明枚举类

    /**
     * 文章状态枚举类
     */
    public enum ArticleStateEnum{
        PUBLISHED,
        DRAFT,
        DELETE;
    }
    

    然后修改DAO接口:

    /**
     * dao接口修改文章状态方法
     * @param articleId 文章ID
     * @param state 状态
     */
    int updateState(int id, ArticleStateEnum state);

    接着Service调用:

    // 修改文章状态为发表
    articleDao.updateState(id, ArticleStateEnum.PUBLISHED);

    以上代码语义清晰,现在传递参数的类型为ArticleStateEnum, 解决了之前描述的两个问题

    枚举的本质

    使用JDK附带工具javap反编译生枚举类字节码, 注javap反编译只会得到public成员:

    看反编译的得到的代码:

    1. class声明,意味着枚举的本质也是类;
    2. 父类声明为java.lang.Enum<>, 意味着枚举类不允许显式使用extends声明父类,包括声明为java.lang.Enum<>也会报错;
    3. 枚举常量,通过public static final修饰符实现,ArticlestateEnum类型声明,意味着所有枚举常量本质是当前枚举类的对象;
    4. values()方法valueOf(String)方法;

    这些转换工作是javac编译器帮我们实现的,JVM并不知道枚举的存在,javac帮我们做了一些语法上的转化、简化程序员编程,这种方式称为语法糖。

    枚举类VS普通类

    枚举类就是类,按照这个逻辑来测试下它和普通类的差别

    添加构造函数:

    红色行提示编译错误“找不到这样的构造函数”,常量声明处添加参数,如下代码正确:

    public enum ArticleStateEnum{
        PUBLISHED(0, "已发布"),
        DRAFT(1, "草稿"),
        DELETE(2, "撤销");
    
        /** 代表的数值 */
        private int value;
        /** 信息提示 */
        private String message;
    
        ArticleStateEnum(int value, String message) {
            this.value = value;
            this.message = message;
        }
    
        // get方法
    }

    可以推测到常量声明的地方,等价于调用构造函数,通常我们都会为字段添加GET方法不添加SET方法,保证枚举常量的不变性。

    枚举类VS普通类的不同点:

    1. 不可以显示声明继承关系;
    2. 常量声明,等价调用构造方法;
    3. 允许有多个构造方法,但修饰符有且仅是private;
    4. 其他地方同类一般无二,可以添加自定添加(方法、字段,抽象成员), 实现接口;

    枚举类VS匿名类

    看看以下如此夸张的写法,也能编译成功:

    观察生成的字节码文件:

    把枚举常量声明的地方替换成构造方法调用new ArticleStateEnum(v1, m1),这不就是匿名类的声明吗!

    现在向枚举类内添加抽象方放,看看结果:

    编译报错“提示有抽象方法未实现”,验证了前面的猜想,这是匿名类的实现,不过不可以显式的使用使用匿名实现枚举类的方式!

    常用方法

    详细参见API文档Enum类:

    public enum ArticleStateEnum{
        PUBLISHED,
        DRAFT,
        DELETE;
    
        public static void main(String[] args) {
            ArticleStateEnum[] states =  ArticleStateEnum.values(); // 1. 获得所有枚举常量
            for(ArticleStateEnum state: states) {
                System.out.println("序号:" + state.ordinal() + " 名字:" + state); //2. 输出声明序号和名称
            }
    
            System.out.println("......................................");
            ArticleStateEnum draft = ArticleStateEnum.valueOf("DRAFT"); //3. 获得某个枚举常量,依据字符串
            if(ArticleStateEnum.DRAFT == draft) {
                System.out.println(ArticleStateEnum.valueOf("DRAFT").name()); //4. name方法输出名字
            }
        }
    }

    输出...

    序号:0 名字:PUBLISHED
    序号:1 名字:DRAFT
    序号:0 名字:DELETE
    
    DRAFT

    JAVA中枚举的缺点

    java中枚举给我们带来强大的语义的时候,又由于枚举常量对象的本质,给我们带了来庞大性,不如C语言的枚举的轻量:

    1. 枚举常量不可以像C语言一样使用移位运算。
    2. 枚举常量和外部交互麻烦,比如:
      • 在mybatis中保存带有枚举字段的实体时,需要你编写转化器(除非按照默认的声明顺序);
      • 转化为JSON数据时;
      • Spring MVC对请求参数封装时,需要自定义转换器;
    3. 枚举常量无法继承,意味着相似的枚举类之间无法继承,导致产生冗余代码;

    建议无特殊情况还是使用枚举常量,毕竟软件的正确性是最重要的

  • 相关阅读:
    项目经验:如何做到不和产品打起来
    leetcode-剑指30-OK
    leetcode-剑指05-OK
    leetcode-剑指58-OK
    leetcode-剑指28-OK
    leetcode-剑指53-I
    leetcode-剑指18-OK
    leetcode-剑指15-OK
    leetcode-剑指27-OK
    leetcode-剑指17-OK
  • 原文地址:https://www.cnblogs.com/zhaohongtian/p/6807094.html
Copyright © 2011-2022 走看看