30.用enum代替int常量
1.枚举类型是指由一组固定的常量组成合法值的类型.
- 不要使用int或String枚举类型
- Java枚举类型是通过公有的静态final域为每个枚举常量导出实例的类, 因为没有可以访问的构造器, 枚举类型是真正的final, 他们是单例的泛型化, 本质上是单元素的枚举.
- 枚举提供了编译时的类型安全
2.将数据与枚举常量关联起来
- 声明私有的final的实例域(枚举天生就是不可变的)
为域提供公有的访问方法
public enum FavoritesType implements ValueEnum {
/**
* 模板库
*/
TEMPLATE(1, "模板库"),
/**
* 图片库
*/
IMAGE(2, "图片库");
private final int value;
private final String caption;
FavoritesType(int value, String caption) {
this.value = value;
this.caption = caption;
}
@Override
public int value() {
return this.value;
}
@Override
public String caption() {
return this.caption;
}
}
3.将行为与枚举常量关联起来
- 定义抽象方法, 常量为类主体, 需要覆盖抽象方法
这种方法被称为特定于常量的方法实现
public enum Operation {
PLUS {
@Override
double apply(double x, double y) {
return x + y;
}
},
MINUS {
@Override
double apply(double x, double y) {
return x - y;
}
},
TIMES {
@Override
double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
@Override
double apply(double x, double y) {
return x / y;
}
};
abstract double apply(double x, double y);
}
4.枚举类型有一个自动产生的valueOf(String)方法, 它将常量的名字转变成常量本身, 如果在枚举类型中覆盖toString, 要考虑编写一个fromString方法, 将定制的字符串表示法变回相应的枚举.
5.使用策略枚举来抽取共享的方法
public enum PayrollDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
private static final int HOURS_PER_SHIFT = 8;
double pay(double hoursWorked, double payRate) {
double basePay = hoursWorked * payRate;
double overtimePay;
switch (this) {
case SATURDAY:
case SUNDAY:
overtimePay = hoursWorked * payRate / 2;
default:
overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;
break;
}
return basePay + overtimePay;
}
}
使用策略枚举优化后:
public enum PayrollDay2 {
MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(
PayType.WEEKDAY), SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
private final PayType payType;
PayrollDay2(PayType payType) {
this.payType = payType;
}
private enum PayType {
WEEKDAY {
@Override
double overtimePay(double hours, double payRate) {
return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT) * payRate / 2;
}
}, WEEKEND {
@Override
double overtimePay(double hours, double payRate) {
return hours * payRate / 2;
}
};
abstract double overtimePay(double hours, double payRate);
private static final int HOURS_PER_SHIFT = 8;
double pay(double hoursWorked, double payRate) {
double basePay = hoursWorked * payRate;
return basePay + overtimePay(hoursWorked, payRate);
}
}
}
31.用实例域代替序数
永远不要根据枚举的序数导出与它关联的值, 而是要将它保存在一个实例域中
32.用EnumSet代替位域
33.用EnumMap代替序数索引
34.用接口模拟可伸缩的枚举
35.注解优先于命名模式
36.坚持使用Override注解
37.用标记接口定义类型
标记接口是没有包含方法声明的接口, 而只是指明一个类实现了具有某种属性的接口, 例如serializable接口.
标记接口有两点胜过标记注解, 首先是标记接口的定义的类型是由被标记类的实例实现的, 标记注解则没有定义这样的类型; 另一个优点是, 标记接口可以被更加精确的进行锁定, 注解只能锁定到类或者方法上, 接口则锁定到特定的类型.