Java Enum类语法和用法解析

一.语法
  1. Enum的全称为enumeration,中文俗称枚举类,学过C/C++等语言的人,应该都对它略知一二. 但在Java语言规范中,是在JDK 5版本中才引入的,存放在 java.lang 包中。在Java版的Enum实质是语法糖,其声明方式如下:

1 package felix.fu 
2 public enum Color{  
3     RED, 
4     BLUE, 
5     BLACK, 
6     YELLOW, 
7     GREEN  
8 } 

  enum是用来声明枚举的关键字,声明定义的类都隐含继承了一个父类(java.lang.Enum<E>),因此枚举不能再继承,但仍可实现接口。 该父类有两个私有属性name(枚举类型的名称)和ordinal(枚举实例被创建的序数),分别通过name()和ordinal()暴露出来了。定义在枚举类型里的每一个枚举实例都将映射到Enum的子类中,其实例的名称和在枚举类型里定义的顺序都会传入到这个构造函数里 protected Enum(String name, int ordinal)。

  2. 如果想更清楚了解一下Enum,可以用反编译工具把自己定义的枚举反编译一下就一目了然了,它就是一个普通的类, 只是Java语言规范从Code角度进行了限制,执行javap felix.fu.Color命令如下:

 1 public final class Color extends java.lang.Enum{ 
 2     public static final Color RED; 
 3     public static final Color BLUE; 
 4     public static final Color BLACK; 
 5     public static final Color YELLOW; 
 6     public static final Color GREEN; 
 7     static {}; 
 8     public static Color[] values(); 
 9     public static Color valueOf(java.lang.String); 
10 } 

  3. 如果定义的枚举有自己的构造函数必须声明私有的。

二.用法
  1.使用场景
  (1) 常用于对同一类常量进行分类
  (2) 声明接口方法时,输入参数类型采用枚举比用原始类型值常量更严谨.
  (3) 常量有时往往不仅仅只是一个值,有可能会包含多个属性,此时很适合用枚举
  (4) 有时常量对象需要从配置文件中读取其描述信息或者UI显示信息,此时也适合用枚举
  (5) 从Java语法层面来说,枚举可以在switch使用,在if中可直接进行比较
  (6) 声明枚举属性时最好用public final修饰,使用起来会非常方便
  (7) 自定义枚举时,建议不要使用自带的name()和ordinal()方法返回值来与原始值类型转换,这样业务不依赖其枚举的名字和顺序,如下:

 1 public enum Color { 
 2     RED(0), BLUE(1), BLACK(2), YELLOW(3), GREEN(4); 
 3     public final int value; 
 4     /** 
 5      * @param value 
 6      */ 
 7     private Color(int value) { 
 8         this.value = value; 
 9     } 
10     public static Color getInstance(int value) { 
11         for (Color code : values()) { 
12             if (code.value == value) 
13                 return code; 
14         } 
15         return null; 
16     } 
17 } 

  2.实例:声明了一个基于枚举的简单异常框架,采用枚举来实现

 1 public interface ExceptionCode { 
 2     /** 
 3      * 返回异常码 
 4      */ 
 5     String getErrorCode();     
 6     /** 
 7      * 返回异常描述 
 8      * @param args 异常描述所用到的占位符值数组 
 9      */     
10     String getErrorCause(Object... args); 
11 } 
12 public enum DbExceptionCode implements ExceptionCode { 
13     UNIQUE_KEY("10001", "主键约束错误"), 
14     FOREIGN_KEY("10002", "外键约束错误"); 
15     public final String errorCode; 
16     public final String errorName; 
17     private DbExceptionCode(String errorCode, String errorName) { 
18         this.errorCode = errorCode; 
19         this.errorName = errorName; 
20     } 
21     @Override 
22     public String getErrorCode() { 
23         return errorCode; 
24     } 
25     @Override 
26     public String getErrorCause(Object... args) { 
27         // TODO need to read from configuration by errorCode 
28         String errorCause = ""; 
29         return MessageFormat.format(errorCause, args); 
30     } 
31 } 
32 public class DbException extends RuntimeException { 
33     protected String errorCode; 
34     protected String errorCause; 
35     /** 
36      * message format:BussinessErrorCode:BussinessErrorCause eg: 10010:无效Barcode[1000052] 
37      * 
38      * @param errorCode   error code of bussiness 
39      * @param errorCause  error cause of bussiness 
40      * @param cause 
41      */ 
42     protected DbException(String errorCode, String errorCause, Throwable cause) { 
43         super(errorCode + ":" + errorCause, cause); 
44         this.errorCode = errorCode; 
45         this.errorCause = errorCause; 
46     } 
47     public DbException(ExceptionCode exceptionCode, Object... args) { 
48         this(exceptionCode.getErrorCode(), exceptionCode.getErrorCause(args)); 
49     } 
50     public DbException(Throwable cause, ExceptionCode exceptionCode, Object... args) { 
51         this(exceptionCode.getErrorCode(), exceptionCode.getErrorCause(args), cause); 
52     } 
53     public DbException(ExceptionCode exceptionCode, Throwable cause) { 
54         this(exceptionCode.getErrorCode(), exceptionCode.getErrorCause(), cause); 
55     } 
56     protected DbException(String errorCode, String errorCause) { 
57         this(errorCode, errorCause, null); 
58     } 
59     /** 
60      * @return the errorCode 
61      */ 
62     public String getErrorCode() { 
63         return errorCode; 
64     } 
65     /** 
66      * @return the errorCause 
67      */ 
68     public String getErrorCause() { 
69         return errorCause; 
70     } 
71 }

三. 弊端: 就是与原始值类似转换起来有点麻烦

四.通常定义常量方法和枚举定义常量方法区别
  以下内容可能有些无聊,但绝对值得一窥
  1.代码:

1 public class State { 
2     public static final int ON = 1; 
3     public static final Int OFF= 0; 
4 } 

  首先,它不是类型安全的。你必须确保是int
  其次,你还要确保它的范围是0 和1
  最后,很多时候你打印出来的时候,你只看到1 和0 ,但其没有看到代码的人并不知道你的企图,抛弃你所有旧的public static final常量.

  2.可以创建一个enum 类,把它看做一个普通的类。除了它不能继承其他类了。(java 是单继承,它已经继承了Enum), 可以添加其他方法,覆盖它本身的方法

  3.switch() 参数可以使用enum 了  
  4.values() 方法是编译器插入到enum 定义中的static 方法,所以,当你将enum 实例向上转型为父类Enum 是,values() 就不可访问了。解决办法:在Class 中有一个getEnumConstants() 方法,所以即便Enum 接口中没有values() 方法,我们仍然可以通过Class 对象取得所有的enum 实例  
  5.无法从enum 继承子类,如果需要扩展enum 中的元素,在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组。达到将枚举元素进行分组。  
  6.使用EnumSet 代替标志。enum 要求其成员都是唯一的,但是enum 中不能删除添加元素。  
  7.EnumMap 的key 是enum ,value 是任何其他Object 对象。
  8.enum 允许程序员为eunm 实例编写方法。所以可以为每个enum 实例赋予各自不同的行为。
  9.使用enum 的职责链(Chain of Responsibility) . 这个关系到设计模式的职责链模式。以多种不同的方法来解决一个问题。然后将他们链接在一起。当一个请求到来时,遍历这个链,直到链中的某个解决方案能够处理该请求。
  10.使用enum 的状态机
  11.使用enum 多路分发

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。