首页 > 学院 > 开发设计 > 正文

[Effective Java]第六章 枚举和注解

2019-11-14 22:45:04
字体:
来源:转载
供稿:网友
[Effective java]第六章 枚举和注解声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.VEVb.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.VEVb.com/jiangzhengjun/p/4255663.html 第六章 枚举和注解30、 用enum代替int常量

枚举类型是指由一组固定的常量组成合法值的类型,例如一年中的季节或一副牌中的花色。在没引入枚举时,一般是声明一组int常量,每个类型成员一个常量:

publicstaticfinalintAPPLE_FUJI= 0;

publicstaticfinalintAPPLE_PipPIN= 1;

publicstaticfinalintAPPLE_GRANNY_SMITH= 2;

publicstaticfinalintORANGE_NAVEL= 0;

publicstaticfinalintORANGE_TEMPLE= 1;

publicstaticfinalintORANGE_BLOOD= 2;

这种方法称作int枚举模式,存在很多不足,不具有类型安全与使用方便性。如果你将apple传到一个想要接收orange的方法中,编译器也不会出现警告,而且还可以使用==来比较apple与orange。

注意每个apple常量都以APPLE_作为前缀,每个orange常量都以ORANGE_作为前缀,这是因为可以防止名称冲突。

采用int枚举模式的程序是十分脆弱,因为int枚举是编译时常量,被编译到使用它们的客户端中。如果与枚举常量关联的int发生了变化,客户端就必须重新编译,如果不重新编译,程序还是可以运行,但不是最新的值了。

另外从使用方便性来看,没有便利的toString方法,打印出来的为数字,没有多大的用处。要遍历一组中所有的int枚举常量,也没有可靠的方法。

既然int枚举常量有这么多的缺点,那使用String枚举常如何?同样也不是我们期望的。虽然在可以打印字符串,但它会导致性能问题,因为它依赖于字符串的比较操作。另外与int枚举常量一样会编译到客户端代码中,编译时难以发现,但会在运行时出错。

幸运的是1.5版本开始,枚举可以避免int和String枚举模式的缺点,并提供许多额外的好处。下面是最简单的形式:

publicenumApple{FUJI,PIPPIN,GRANNY_SMITH}

publicenumOrange{NAVEL,TEMPLE,BLOOD}

Java枚举类型背后的基本想法很简单:本质上是int值,它们是通过公有的静态final域为每个枚举常量导出实例的类。因为没有可以访问的构造器,枚举类型是真正的final。因为客户端即不能创建枚举类型的实例,也不能对它进行扩展,因此对它进行实例化,而只有声明过的枚举常量。换句话说,枚举类型是实例受控的。它们是单例的泛型化,本质上是单元素的枚举。

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

枚举提供了单独的命名空间,同一系统中可以有多个同名的枚举类型变量。你可以增加或者重新排序枚举类型常量,而无需重新编译它的客户端代码,因为导出常量的域在枚举类型和它的客户端之间提供了一个隔离层:常量值并没有被编译到客户端代码中,而是在int枚举模式之中。最终,可以通过调用toString方法,将枚举转换成可打印的字符串。

除了完善了int枚举模式不足外,枚举还允许添加任意的方法和域,并实例任意接口,它们提供了所有Object(见第3章)的高级实现,实现了Comparable和Serializable接口,并针对枚举型的可任意改变性设计了序列化方式。

如果一个枚举具有普遍适用性,它就应该成为一个顶层类,如果它只是被用在一个特定的顶层类中,它就应该成为该顶层类的一个成员类。

可以为枚举类型添加数据域与方法,下面是一个算术运行的枚举类:

publicenumOperation {

PLUS("+") {

doubleapply(doublex,doubley) {

returnx + y;

}

},

MINUS("-") {

doubleapply(doublex,doubley) {

returnx - y;

}

},

TIMES("*") {

doubleapply(doublex,doubley) {

returnx * y;

}

},

DIVIDE("/") {

doubleapply(doublex,doubley) {

returnx / y;

}

};

PRivatefinalString symbol;//操作符:+ - * /

Operation(String symbol) {//构造函数,存储操作符供toString打印使用

this.symbol = symbol;

}

@Override

//重写Enum中的打印name的性为

publicString toString() {

returnsymbol;

}

//抽像方法,不同的常量具有不同的功能,需在每个常量类的主体里重写它

abstractdoubleapply(doublex,doubley);

/*

* 初始化时,存储操作符与枚举常量的对应关系,用来实现fromString方法

* 这样我们就可以通过操作符来获取到对应的枚举常量,有点像valueOf方法,

* 只不过它是通过枚举常量的名字name来获取常量的。这种通用的方法还可以

* 应用到其他枚举类中

*/

privatestaticfinalMap<String, Operation>stringToEnum=newHashMap<String, Operation>();

static{ //从name到枚举常量转换到从某个域到枚举常量的转换

for(Operation op :values())

stringToEnum.put(op.toString(), op);

}

//根据操作符来获取对应的枚举常量,如果没有返回null,模拟valueOf方法

publicstaticOperation fromString(String symbol) {

returnstringToEnum.get(symbol);

}

publicstaticvoidmain(String[] args) {

doublex = Double.parseDouble(args[0]);

doubley = Double.parseDouble(args[1]);

for(Operation op : Operation.values())

System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));

for(Operation op : Operation.values())

System.out.printf("%f %s %f = %f%n", x, op, y, Operation

.fromString(op.toString()).apply(x, y));

}

}

在opr包下会看见Operation.class、Operation$4.class、Operation$2.class、Operation$3.class、Operation$1.class这样几个类,Operation$X.class都是继承自Operation类,而Operation又继承自Enum类,下面是反编译这些类的代码:

public abstract class opr.Operation extends java.lang.Enum{

Word-spacing: 0px; text-transform: none; word-break: normal; margin: 0cm 0cm 0pt; letter-spacing: normal; line-height: normal; text-indent: 0px; -webkit-text-size

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表