用enum代替int常量。

来源:互联网 发布:sublimetext3 mac 编辑:程序博客网 时间:2024/05/01 21:52

枚举类型(enum type)是指由一组固定的常量组成合法值得类型,例如一年中的季节,太阳系中的行星或者一副牌中的花色。java的枚举本质上是int值。

java枚举类型背后的基本想法非常简单:他们就是通过公有的静态final域为每个枚举常量导出实例的类。因为没有可以访问的构造器,枚举类型是真正的final。因为客户端既不能创建枚举类型的实例,也不能对他进行扩展,因此很可能没有实例,而只是声明过的枚举常量。换句话说,枚举类型是实例受控的。他们是单例(Singleton)的泛型化,本质上是单元素的枚举。枚举类型为类型安全的枚举(typesafe enum)模式,提供了语言方法的支持。

枚举提供了编译时的类型安全。如果声明一个参数的类型为Apple,就可以保证,被传到该参数上的任何非null的对象引用一定属于三个有效的Apple值之一。试图传递类型错误的值时,会导致编译时错误,就像试图将某种枚举类型的表达式赋给另一种枚举类型的变量,或者试图利用==比较不同枚举类型的值一样。

包含同名常量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间。你可以增加或者重新排列枚举类型中的常量,而无需重新编译它的客户端代码,因为导出常量的域在枚举类型和它的客户端之间提供了一个隔离层:常量值并没有被编译到客户端代码中,而是在int枚举模式之中。最终,可以通过调用toString方法,将枚举转换成可打印的字符串。
除了完善了int枚举模式的不足之处,枚举类型还允许添加任意的方法和域,并实现任意的接口。他们提供了所有Object方法的高级实现,实现了Comparable和Serializable接口,并针对枚举类型的可任意改变性设计了序列化方式。

那么我们为什么要将方法或者域添加到枚举类型中呢?首先,你可能是想将数据与他的常量关联起来。例如,一个能够返回水果颜色或者返回水果图片的方法,对于我们的Apple和Orange类型来说可能很有好处,你可以利用任何适当地方法来增强枚举类型。枚举类型可以先作为枚举常量的一个简单集合,随着时间的推移再演变成为全功能的抽象。

为了将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。枚举天生就是不可变的,因此所有的域都应该为final的。他们可以是公,但最好将他们做成是私有的,并提供公有的访问方法。

注意枚举类都有一个静态的values方法,按照声明顺序返回它的值数组。还要注意toString方法返回每个枚举值得声明名称,使得println和printf的打印变得更加容易。如果你不满意这种字符串表示法,可以通过覆盖toString方法对它进行修改。

与枚举常量关联的有些行为,可能只需要用在定义了枚举的类或者包中。这种行为最好被实现成私有的或者包级私有的方法。于是,每个枚举常量都带有一组隐藏的行为,这使得包含该枚举的类或者包在遇到这种常量时都可以做出适当地反应。就像其他的类一样,除非迫不得已要将枚举方法导出至它的客户端。否则都应该将它声明为私有的,如有必要,则声明为包级私有的。

如果一个枚举具有普遍适用性,他就应该成为一个顶层类(top-level class);如果它只是被用在一个特定的顶层类中,他就应该成为该顶层类的一个成员类。例如java.math.RoundingMode枚举表示十进制小数的舍入模式(rounding mode)。这些舍入模式用于BigDecimal类,但是他们提供了一个非常有用的抽象,这种抽象本质上又不属于BigDecimal。通过使RoundingMode变成一个顶层类,库的设计者孤立任何需要舍入模式的程序员重用这个枚举,从而增强API之间的一致性。

有一种方法可以将不同的行为与每个枚举常量关联起来:在枚举类型中声明一个抽象的方法,并在特定于常量的类主体(constant-specific class body)中,用具体的方法覆盖每个常量的抽象方法,这种方法被称作特定于常量的方法实现(constant-specific method implementation)。

枚举类型有一个自动产生的valueOf(String)方法,它将常量的名字转变成常量本身。如果在枚举类型中覆盖toString,要考虑编写一个fromString方法,将定制的字符串表示法变回相应的枚举。

你真正想要的就是每当添加一个枚举常量时,将强制选择一种策略。幸运的是,有一种很好地方法可以实现这一单。这种方法就是把策略移到一个私有的嵌套枚举中,将这个策略枚举(strtegy enum)的实例传到枚举的构造其中。之后枚举类将策略委托给策略枚举,枚举类中就不需要switch语句或者特定于常量的方法实现了。虽然这种模式没有switch语句那么简洁,但更加安全,也更加灵活。

如果枚举中的switch语句不是在枚举中实现特定于常量的行为的一种很好地选择,那么他们还有什么用处呢?枚举中的switch语句适合于给外部的枚举类型增加特定于常量的行为。

一般来说,枚举会优先使用comparable而非int常量。与int常量相比,枚举有个小小的性能缺点,即装载和初始化枚举时会有空间和时间的成本。除了受资源约束的设备,例如手机和烤面包机之外,在实践中不必太在意这个问题。

那么什么时候应该使用枚举呢?每当需要一组固定常量的时候。当然,这包括“天然的枚举类型”,例如行星、一周的天数以及棋子的数目等等。但它也包括你在编译时就知道其所有可能值的其他集合,例如菜单的选项,操作代码以及命令行标记等。枚举类型中的常量集并不一定要始终保持不变。专门设计枚举特性是考虑到枚举类型的二进制兼容演变。

总而言之,与int常量相比,枚举类型的优势是不言而喻的。枚举要易读得多,也要更安全,功能更加强大。许多枚举都不需要显式的构造器或者成员,但许多其他枚举则受益于“每个常量与属性关联”以及“提供行为受这个属性影响的方法”。只有极少数的枚举受益于将多种行为与单个方法关联。在这种相对少见的情况下,特定于常量的方法要优先于启用自有值的枚举。如果多个枚举常量同时共享相同的行为,则考虑策略枚举。

原创粉丝点击