枚举类型

1、基本的enum特性

  • 关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用。

  • 调用enum的values()方法,可以遍历enum实例,返回的是enum实例的数组,而且该数组中的元素严格保持其在enum中声明时的顺序。

  • 创建enum时,编译器会生成一个相关的类,这个类继承自java.lang.Enum。

  • Enum提供的一些功能:

    ordinal():返回一个int值,这是每个enum实例在声明时的次序,从0开始。

    ==:比较enum实例,编译器会自动提供equals()和hashCode()方法。

    Enum类实现了Comparable接口,具有compareTo()方法,同时它还实现了Serializable接口。

    enum实例上调用getDeclaringClass()方法,就能知道其所属的enum类。

    name():返回enum实例声明时的名字,这与使用toString()方法效果相同。

    valueOf():Enum中定义的static方法,它根据给定的名字返回相应的enum实例,如果不存在,将抛出异常。

  • 静态导入用于enum。使用static import能够就将enum实例的标识符带入当前的命名空间,无需再用enum类型来修饰enum实例。多数情况下,使用static import还是有好处的,不过程序员还是应该对具体情况进行具体分析。

2、向enum中添加新方法

  • 不能继承自一个enum之外,基本上可将enum看作一个常规的类,可以向enum中添加方法,甚至可有main()方法。

  • 打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号,同时Java要求必须先定义enum实例,如果在定义enum实例之前定义了任何方法和属性,在编译时会报错。

  • 有意识地将enum的构造器声明为private,只能在enum定义的内部使用其构造器创建enum实例。

  • 覆盖enum的方法。覆盖enum的toString()方法与覆盖一般类的方法没有区别。

3、switch语句中的enum

  • *在switch中使用enum,是enum提供的一项非常便利的功能。一般来说,在switch中只能使用整数值,而枚举实例天生就具备整数值的次序,并可通过ordinal()方法取得其次序(显然编译器帮我们做了类似的工作)。

  • 一般情况下必须使用enum类型来修饰一个enum实例(除了静态导入),但是在case语句中却不必如此。

  • switch语句中没有default语句,编译器不会报错(少了分支也不会报),因此必须确保覆盖多有分支。但如果case语句中调用return,缺少default编译器就会报错。

4、values()的神秘之处

  • 编译器创建的enum类都继承自Enum类,然而Enum类并没有values()方法。

  • *values()方法是由编译器创建enum类的过程中添加的static方法,编译器将创建的enum类标记为final类,所以无法继承。

  • 由于values()方法是由编译器插入到enum定义中的static方法,因此将enum实例向上转型为Enum,values()方法就不可访问了。不过在Class中有一个getEnumConstants()方法,因此即便Enum接口中没有values()方法,仍可以通过Class对象取得所有Enum实例。

5、实现,而非继承

  • 所有的enum都继承自java.lang.Enum类,由于Java不支持多重继承,因此enum不能再继承其他类,然而可以同时实现一个或多个接口。

6、随机选取

  • 枚举类型

7、使用接口组织枚举

  • 无法从enum继承子类有时很令人沮丧,有时希望使用子类将一个enum中的元素进行分组。

  • 达到将枚举元素分类组织的目的,可以在一个接口内部,创建实现该接口的枚举类,以此来将元素进行分组。如果想创建一个“枚举的枚举”,可以创建一个新的enum,然后用其实例包装接口中的每一个enum类。

  • 还有一种更简洁的管理枚举的方法,就是将enum嵌套在另一个enum内,把上述所说的接口放在枚举类的内部。

8、使用EnumSet替代标志

  • Set是一种集合,只能向其中添加不重复的对象,enum也要求其成员都是唯一的。

  • Java SE5引入EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“位标志”,这种标志可以用来表示某种“开/关”信息,不过使用这种标志,很容易写出令人难以理解的代码。

  • EnumSet的设计充分考虑了速度因素,因为它必须与非常高效的bit标志相竞争,就其内部而言,它(可能)就是将long值作为比特向量,因此EnumSet非常快速高效。使用EnumSet的优点是,它在说明一个二进制位是否存在时,具有更好的表达能力和性能。

  • EnumSet中的元素必须来自一个enum。

  • EnumSet提供的一些静态方法:

    noneOf(Class<E> elementType):创建一个具有指定元素类型的空枚举set。

    allOf(Class<E> elementType):创建一个包含指定元素类型的所有元素的枚举set。

    complementOf(EnumSet<E> s):创建一个其元素类型与指定枚举 set 相同的枚举set,最初包含指定set中所不包含的此类型的所有元素(补集)。

    of(E... e):可以多个元素。

    range(E from,E to)创建一个最初包含由两个指定端点所定义范围内的所有元素的枚举 set。返回的 set 将包含端点本身,这两个端点可能相同,但顺序不能颠倒。

  • EnumSet的基础是long,一个long值有64位,而一个enum实例只需一位bit表示其是否存在,也就是在不超过一个long的表达能力的情况下,EnumSet可应用于最多不超过64个元素的enum。但是enum超过64个元素,EnumSet貌似会在必要时增加一个long,因此EnumSet还是可应用于多过64个元素的enum。

9、使用EnumMap

  • EnumMap是一种特殊的Map,它要求其中的键(key)必须来自一个enum。由于enum本身的限制,EnumMap在内部作为数组实现,因此EnumMap的速度很快,可放心地使用enum实例在EnumMap中进行查询操作。不过只能将enum的实例作为键来调用put()方法,其他操作与使用一般的Map差不多。

  • 与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。

  • 与常量相关的方法constant-specific methods相比EnumMap优点是允许程序员改变值对象,而常量相关的方法在编译期就被固定了。

  • 可使用EnumMap实现多路分发(multiple dispatching)。

10、常量相关的方法

  • java的enum有个非常有趣的特性,即它允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。要实现常量相关的方法,需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。

  • 通过常量相关的方法,每个实例可具备自己独特的行为,这似乎说明每个enum实例(static final)就像一个独特的类,然而enum实例与类的相似之处仅限于此,并不能真的将enum实例作为一个类型使用。

  • 程序员也可以覆盖常量相关的方法。

  • 通过常量相关的方法,可以很容易实现一个简单的职责链。在职责链设计模式中,程序员以多种不同的方式来解决一个问题,然后将它们链接在一起,当一个请求到来时,它遍历这个链,知道链中的某个解决方案能够处理该请求。

  • 枚举类型非常适合用来创建状态机。一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下一个状态,不过也可能存在瞬时状态,而一旦任务执行结束,状态机会立刻离开瞬时状态。由于enum对其实例有严格限制,非常适合用来表现不同的状态和输入。

11、多路分发

  • 当处理多种交互类型时,程序可能会变得相当杂乱。Java只支持单路分发,也就是说如果执行的操作包含了不止一个类型未知的对象时,那么Java的动态绑定机制只能处理其中一个的类型,这就无法解决问题,因此必须要来判定其他的类型,从而实现自己的动态绑定。

  • 解决上述问题的办法是多路分发。多态只能发生在方法调用时,因此如果使用两路分发,就必须有两个方法调用:第一个方法调用决定第一个未知类型,第二个方法调用决定第二个未知类型。为了达这种效果,我们需要与多个方法一同工作:因为每个分发都需要一个方法的调用。

你可能感兴趣的:(枚举类型)