30.用enum代替int常量

1.枚举类型是指由一组固定的常量组成合法值的类型.

  • 不要使用int或String枚举类型
  • Java枚举类型是通过公有的静态final域为每个枚举常量导出实例的类, 因为没有可以访问的构造器, 枚举类型是真正的final, 他们是单例的泛型化, 本质上是单元素的枚举.
  • 枚举提供了编译时的类型安全

2.将数据与枚举常量关联起来

  • 声明私有的final的实例域(枚举天生就是不可变的)
  • 为域提供公有的访问方法

    1. public enum FavoritesType implements ValueEnum {
    2. /**
    3. * 模板库
    4. */
    5. TEMPLATE(1, "模板库"),
    6. /**
    7. * 图片库
    8. */
    9. IMAGE(2, "图片库");
    10. private final int value;
    11. private final String caption;
    12. FavoritesType(int value, String caption) {
    13. this.value = value;
    14. this.caption = caption;
    15. }
    16. @Override
    17. public int value() {
    18. return this.value;
    19. }
    20. @Override
    21. public String caption() {
    22. return this.caption;
    23. }
    24. }

3.将行为与枚举常量关联起来

  • 定义抽象方法, 常量为类主体, 需要覆盖抽象方法
  • 这种方法被称为特定于常量的方法实现

    1. public enum Operation {
    2. PLUS {
    3. @Override
    4. double apply(double x, double y) {
    5. return x + y;
    6. }
    7. },
    8. MINUS {
    9. @Override
    10. double apply(double x, double y) {
    11. return x - y;
    12. }
    13. },
    14. TIMES {
    15. @Override
    16. double apply(double x, double y) {
    17. return x * y;
    18. }
    19. },
    20. DIVIDE {
    21. @Override
    22. double apply(double x, double y) {
    23. return x / y;
    24. }
    25. };
    26. abstract double apply(double x, double y);
    27. }

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

5.使用策略枚举来抽取共享的方法

  1. public enum PayrollDay {
  2. MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
  3. private static final int HOURS_PER_SHIFT = 8;
  4. double pay(double hoursWorked, double payRate) {
  5. double basePay = hoursWorked * payRate;
  6. double overtimePay;
  7. switch (this) {
  8. case SATURDAY:
  9. case SUNDAY:
  10. overtimePay = hoursWorked * payRate / 2;
  11. default:
  12. overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;
  13. break;
  14. }
  15. return basePay + overtimePay;
  16. }
  17. }

使用策略枚举优化后:

  1. public enum PayrollDay2 {
  2. MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(
  3. PayType.WEEKDAY), SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
  4. private final PayType payType;
  5. PayrollDay2(PayType payType) {
  6. this.payType = payType;
  7. }
  8. private enum PayType {
  9. WEEKDAY {
  10. @Override
  11. double overtimePay(double hours, double payRate) {
  12. return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT) * payRate / 2;
  13. }
  14. }, WEEKEND {
  15. @Override
  16. double overtimePay(double hours, double payRate) {
  17. return hours * payRate / 2;
  18. }
  19. };
  20. abstract double overtimePay(double hours, double payRate);
  21. private static final int HOURS_PER_SHIFT = 8;
  22. double pay(double hoursWorked, double payRate) {
  23. double basePay = hoursWorked * payRate;
  24. return basePay + overtimePay(hoursWorked, payRate);
  25. }
  26. }
  27. }

31.用实例域代替序数

永远不要根据枚举的序数导出与它关联的值, 而是要将它保存在一个实例域中

32.用EnumSet代替位域

看不懂, 略

33.用EnumMap代替序数索引

34.用接口模拟可伸缩的枚举

35.注解优先于命名模式

36.坚持使用Override注解

37.用标记接口定义类型

标记接口是没有包含方法声明的接口, 而只是指明一个类实现了具有某种属性的接口, 例如serializable接口.
标记接口有两点胜过标记注解, 首先是标记接口的定义的类型是由被标记类的实例实现的, 标记注解则没有定义这样的类型; 另一个优点是, 标记接口可以被更加精确的进行锁定, 注解只能锁定到类或者方法上, 接口则锁定到特定的类型.