这几天在阅读《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: [email protected] * @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: [email protected] * @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: [email protected] * @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)); } }