枚举

枚举类型是java 5中新增特性的⼀部分,它被用来将⼀组类似的值包含到⼀种类型当中,
面这种枚举类型的名称则会被定义成独⼀⽆⼆的类型描述符,在这⼀点上和常量的定义相似。
不过相比较常量类型,枚举类型可以为申明的变量提供更⼤的取值范围。

以前定义⼀个常量是在类或接口中声明,比如下面的代码:

public class Color{
    public static final int RED = 0;
    public static final int GREEN = 1;
    public static final int BLACK = 1;
}

但这种方式主要的问题有如下⼏点:

  • 类型不安全: 因为这些常量本质上还是整数,你仍然可以传入任意整数类型的值,这样是可能导致错误的.
  • 一致性差: 因为整形枚举属于编译期常量,
    所以编译过程完成后,所有客户端和服务器端引用的地方,

会直接将整数值写入。这样,当你修改旧的枚举整数值后或者增加新的枚举值后,
所有引用地方代码都需要重新编译,否则运行时刻就会出现错误。

  • 类型无指意性:由于颜色枚举值仅仅是⼀些无任何含义的整数值,如果在运行期调试时候,
    你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。

基本使用

枚举不仅是简单地将整形数值转换成对象,而是将枚举类型定义转变成⼀个完整功能的类定义。
这种类型定义的扩展允许开发者给枚举类型增加任何方法和属性,也可以实现任意的接口。
另外,自定义的枚举类型默认继承了Enum这个抽象类,此抽象类默认实现 Comparable 和 Serializable 接口。
由于定义的枚举继承了Enum类,会从此类继承⼀些有用的方法,常⻅的方法如下:

  • ordinal:实例方法,返回枚举项的序号,也就是在枚举声明中的序号,第⼀项的值为0,依次递增.
  • name:实例方法,返回枚举常量的名称
  • valueOf:静态方法,依据枚举常量得到对应的枚举对象
  • values:静态方法,返回每⼀个枚举常量代表的枚举对象
public enum Color {
    /**
     * 颜色
     */
    RED("红色"),GREEN("绿色"),BLACK("黑色");

    /**
     * 颜色名称
     */
    private final String colorName;

    Color(String colorName) {
        this.colorName = colorName;
    }

    public String getColorName() {
        return colorName;
    }
}

添加抽象方法或者实现接口

枚举类中可以添加抽象方法,由于每⼀个枚举类型都是final的,所以此抽象方法的实现地方就是枚举类本身,并且每⼀个枚举常量都需要实现此方法。
由于枚举继承于Enum,它是个类,所以其也也可实现接口,让枚举实现接口的⼀种典型用法是利用接口组织各种枚举类型。
例如:

public enum Color {
    /**
     * 颜色
     */
    RED("红色"){
        @Override
        public String getColorRemark(String user) {
            return user + "喜欢红色";
        }
    },GREEN("绿色"){
        @Override
        public String getColorRemark(String user) {
            return user + "喜欢绿色";
        }
    },BLACK("黑色"){
        @Override
        public String getColorRemark(String user) {
            return user + "喜欢黑色";
        }
    };

    /**
     * 颜色名称
     */
    private final String colorName;

    Color(String colorName) {
        this.colorName = colorName;
    }

    public String getColorName() {
        return colorName;
    }

    /**
     * 获取颜色备注的方法
     * @return 颜色备注
     */
    public abstract String getColorRemark(String user);

    /**
     * 方便通过值获取对象
     * @param type 值
     * @return 对象
     */
    public static Color getEnum(String type){
        Color result = null;
        Color[] colors = Color.values();
        for (Color color : colors) {
            if (color.getColorName().equals(type)) {
                result = color;
            }
        }
        MyAssert.isNotNull(result, "Get type: "+type+" Color fails");
        return result;
    }
}

这是一个添加抽象方法的枚举,使用这样的枚举能有效地避免多次if...else或者switch的使用和定义。
接口和抽象方法操作相似。

public class Main {
  private static final String RED_ZH_NAME = "红色";
  private static final String GREEN_ZH_NAME = "绿色";
  public static void main(String[] args) {
    String colorName = "黑色";
    String user = "超人不会飞";
    String value =  Color.getEnum(colorName).getColorRemark(user);
        /*
            value ==> 超人不会飞喜欢黑色
            相当于
            if(RED_ZH_NAME.equals(colorName)) {
                value = user + "喜欢红色";
            }else if (GREEN_ZH_NAME.equals(colorName)){
                value = user + "喜欢绿色";
            }else {
                value = user + "喜欢黑色";
            }
        */
  }
}

EnumSet

JDK5.0 中在增加 Enum 类的同时,也增加了两个工具类 EnumSet 和 EnumMap,这两个类都放在 java.util 包中。
EnumSet 是⼀个针对枚举类型的高性能的 Set 接口实现。此类是⼀个抽象类,主要是使⽤其静态方法来操作各种枚举类型,
主要的静态方法如下:

  • range: 创建⼀个包含枚举值中指定范围的枚举对象集合
  • allOf:创建⼀个枚举所有常量值代表的对象集合
  • noneOf: 创建⼀个指定枚举类型的空集合
  • of: 创建⼀个包含方法参数指定的所有元素的集合
  • copyOf: 创建⼀个参数集合中的所有元素的集合

EnumMap

EnumMap 也是⼀个高性能的 Map 接口实现,用来管理使用枚举类型作为 keys 的映射表,并且键不能允许为null。
EnumMap与普通的Map集合使用起来基本差不多,但它是⼀个专⻔高效处理枚举作为键的高效Map集合实现。

Class对象中的枚举

class对象中关于枚举的相关功能主要是getEnumConstants方法与isEnum方法.

  • getEnumConstants方法返回枚举类型的所有元素,如果class对象不是枚举类型返回null,此方法作用类似枚举类型的values方法。
  • isEnum方法用来判断是否是⼀个枚举类型。

使用注意

  1. enum 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数⼀定要是 private 或 friendly的。
    也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
  2. 定义 enum 类型时候,如果是简单类型,那么最后⼀个枚举值后不用跟任何⼀个符号;
    但如果有定制方法,那么最后⼀个枚举值与后面代码要用分号 ; 隔开,不能用逗号或空格。
  3. 由于 enum 类型的值实际上是通过运行期构造出对象来表示的,所以在 cluster 环境下,每个虚拟机都会构造出⼀个同义的枚举对象。
    因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似⼀样的枚举值⼀定不相等,因为这不是同⼀个对象实例。
  4. 枚举类是不能被继承的,因为每⼀个枚举类都是final的