这几天在阅读《effective java》一书中enum相关的章节。笔记如下:
下面的例子中,提供了四种枚举型常量,这些枚举常量含有可以进行加减乘除操作的方法。
public enum Opration_V1 {
PLUS,MINS,TIMES,DIVIES;
double apply(double x,double y){
switch (this) {
case PLUS:
return x+y;
case MINS:
return x-y;
case TIMES:
return x*y;
case DIVIES:
return x/y;
}
throw new AssertionError();
}
}
修改上述代码如下:
public enum Opration_V1 {
PLUS,MINS,TIMES,DIVIES,
ERROR;
double apply(double x,double y){
switch (this) {
case PLUS:
return x+y;
case MINS:
return x-y;
case TIMES:
return x*y;
case DIVIES:
return x/y;
}
throw new AssertionError();
}
}
写道
上述代码中添加了一个新的枚举常量error,但是没有添加相应的apply()方法,因此在运行时调用Opration_V1.ERROR.apply()会报错。这种错误在编译期间不会出现,只是在运行期出现。
为了解决上述问题,可以采用如下的方法:
public enum Opration_V2 {
PLUS{double apply(double x, double y) {return x+y;}},
MINS{double apply(double x, double y) {return x-y;}},
TIMES{double apply(double x, double y) {return x*y;}},
DIVIES{double apply(double x, double y) {return x/y;}};
abstract double apply(double x,double y);
}
枚举类Opration_V2中添加了一个抽象方法apply();对于具体的枚举成员,都需要实现这个抽象方法。因此添加新的枚举成员的时候,编辑器会自动提示用户添加相应的apply方法。在编译期间就解决版本1中的问题。
可以对版本2进一步优化。
package com.enumDemo;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* @author E-mail: hwy1782@gmail.com
* @date : 2013-1-30 04:16:24
*/
public enum Opration_V3 {
PLUS("+"){double apply(double x, double y) {return x+y;}},
MINS("-"){double apply(double x, double y) {return x-y;}},
TIMES("x"){double apply(double x, double y) {return x*y;}},
DIVIES("/"){double apply(double x, double y) {return x/y;}};
private String symbol;
private Opration_V3(String symbol) {
this.symbol = symbol;
}
abstract double apply(double x,double y);
@Override
//use symbol to display enum
public String toString() {
//Returns the name of this enum constant, as contained in the declaration
return symbol;
}
/**
* get enum of the symbol
* @param symbol
* @return
*/
public static Opration_V3 formString(String symbol){
return stringToEnum.get(symbol);
}
private static final Map<String,Opration_V3> stringToEnum = Maps.newHashMap();
static{
for(Opration_V3 op : Opration_V3.values()){
stringToEnum.put(op.toString(), op);
}
}
}
上述部分主要进行了如下几点优化:
1.将枚举常量与特定的符号关联起来,通过重写enum的toSting()方法,对外展示枚举常量的symbol
2.添加一个fromString()方法,返回symbol对应的枚举常量。
上面的例子还存在一些问题。版本3中每一个enum类型对应一个apply()方法,但是如果存在一种情况:几个enum常量对应相同的apply()方法,是不是每个enum常量都把相应的apply()方法再拷贝一遍呢?下面有一种解决方案:采用switch的方式,匹配对应的方法:
package com.enumDemo;
/**
* @author E-mail: hwy1782@gmail.com
* @date : 2013-1-31 上午11:32:51
* Payroll 枚举工资单,计算每天的工资
*
* </br>缺点:
* </br>
* 添加一个新的enmu,忘记在pay()方法中添加相应的switch条件的话,就会采用default的计算pay的方法。
*
*/
public enum PayrollDay_v1 {
MONDAY,TUESDAY,WEDNESDAY,THRSDAY,FRIDAY,SAYURDAY,SUNDAY;
private static final int DEFAUL_WORK_HOUR = 8;
double pay(double workHoues,double payRate){
double basicPay = DEFAUL_WORK_HOUR * payRate;
double overTimePay = 0;
switch (this) {
case SAYURDAY: case SUNDAY:
overTimePay = workHoues * payRate * 2;
default:
overTimePay = (workHoues - DEFAUL_WORK_HOUR) * payRate * 2;
}
return basicPay + overTimePay;
}
}
但是上述方案同样存在一些问题,新增一个enm常量,但是忘记添加相应的switch条件,则这个新增的enum会执行默认的方法,存在很大的隐患。
下面提供了一种解决方案,枚举策略方法:
package com.enumDemo;
/**
* @author E-mail: hwy1782@gmail.com
* @date : 2013-1-31 上午11:32:51
* Payroll 枚举工资单,计算每天的工资
*
* </br>改进点:
* </br>
* 修复版本1中存在的缺陷:
* 新增一个枚举的时候忘记添加相应的计算工资的方法。</br>
* 方法:
* 1.添加一个abstract 方法,每个枚举常量必须实现这个abstract方法,在这个abstract方法中实现每个enum常量的pay()方法。
* 但是上述的方法有一个缺陷,每个枚举常量必须都实现这个abstract方法,不能做到这个方法的复用。
*
* 2.此处使用策略枚举(strategy enum)来解决这个问题
* 枚举策略的使用条件是:
* <br/>
* 枚举常量中: 存在多个常量共用一个方法的时候,抽象公用方法为枚举
* <br/>
* 可以将这种策略枚举添加在外部枚举常量的构造函数中,解决新加常量时遗忘添加方法的问题
*
*/
public enum PayrollDay_v2 {
MONDAY(PayType.WEEKDAY),
TUESDAY(PayType.WEEKDAY),
WEDNESDAY(PayType.WEEKDAY),
THRSDAY(PayType.WEEKDAY),
FRIDAY(PayType.WEEKDAY),
SAYURDAY(PayType.WEEKDAY),
SUNDAY(PayType.WEEKEND);
private final PayType payType;
PayrollDay_v2(PayType type){
this.payType = type;
}
public double pay(double workHourse,double payRate){
return payType.pay(workHourse, payRate);
}
//策略枚举
private enum PayType{
WEEKDAY{
@Override
double overtimePay(double workHourse,double payRate) {
return workHourse > DEFAUL_WORK_HOUR ? 0 : (workHourse - DEFAUL_WORK_HOUR)*payRate;
}
},
WEEKEND{
@Override
double overtimePay(double workHourse,double payRate) {
return workHourse * payRate;
}
};
static final int DEFAUL_WORK_HOUR = 8;
abstract double overtimePay(double workHourse,double payRate);
double pay(double workHourse,double payRate){
double basicPay = workHourse * payRate;
return basicPay + overtimePay(workHourse, payRate);
}
}
}
测试类如下:
package com.enumDemo;
import org.junit.Test;
/**
*
* @author hongye.hwy
*
*/
public class PayrollDay_v2_Drive {
private static final double DEFAULT_PAY_RATE = 9.8;
@Test
public void test_PayrollDay() {
double workHourse = 8;
System.out.printf("%s work %f hours will get %f$",
PayrollDay_v2.SAYURDAY,workHourse,PayrollDay_v2.SAYURDAY.pay(8, DEFAULT_PAY_RATE));
}
}