Effective Java 学习笔记之枚举

ゞ 浴缸里的玫瑰 2022-06-14 06:41 316阅读 0赞

1.用enum代替int常量

1)为了将数据和枚举常量关联起来 得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。
2)枚举天生就是不可变的,因此所有的域都应该是final的。它们可以是公有的,但是最好做成私有并提供公有的访问方式
3)枚举类型还允许添加任意的方法和域 并实现任意的接口
4)提供了所有Object方法的高级实现 实现了Comparable和Serializable接口,并设计了序列化方式

  1. public enum Planet {
  2. MERCURY(3.302E+23, 2.439E6),
  3. VENUS(4.869e+24, 6.052e6),
  4. EARTH(5.975e+24, 6.378e6),
  5. MARS(6.419e+23, 3.393e6),
  6. JUPITER(1.899e+27, 7.149e7),
  7. SATURN(5.685e+26, 6.027e7),
  8. URANUS(8.683e+25, 2.556e7),
  9. NEPTUNE(1.024e+26, 2.477e7);
  10. private final double mass;
  11. private final double radius;
  12. private final double surfaceGravity;
  13. private static final double G = 6.67300E-11;
  14. Planet(double mass, double radius) {
  15. this.mass = mass;
  16. this.radius = radius;
  17. surfaceGravity = G * mass / (radius * radius);
  18. }
  19. public double getMass() {
  20. return mass;
  21. }
  22. public double getRadius() {
  23. return radius;
  24. }
  25. public double getSurfaceGravity() {
  26. return surfaceGravity;
  27. }
  28. public double getSurfaceWeight(double mass) {
  29. return mass * surfaceGravity;
  30. }
  31. }

扩展:需要为每个常量添加不同的行为

  1. public enum Operation1 {
  2. PLUS, MINUS, TIMES, DIVIDE;
  3. double apply(double x, double y) {
  4. switch (this) {
  5. case PLUS:
  6. return x + y;
  7. case MINUS:
  8. return x - y;
  9. case TIMES:
  10. return x * y;
  11. case DIVIDE:
  12. return x / y;
  13. }
  14. throw new AssertionError("Unknown op:" + this);
  15. }
  16. }

上面的代码存在一些问题,当为枚举类添加新的常量后,忘记为其提供相应apply的实现,编译不会出现错误提示,然而运行结果却不是希望的

因此可以进行修改

提供一个抽象的apply方法,这样每个常量都必须实现这个抽象方法,否则编译就会报错

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

有时候复写toString方法也会非常有用

  1. public enum Operation2 {
  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. private String symbol;
  28. Operation2(String symbol) {
  29. this.symbol = symbol;
  30. }
  31. @Override
  32. public String toString() {
  33. return symbol;
  34. }
  35. }
  36. public class OpeartionTest {
  37. public static void main(String[] args) {
  38. double d1 = 1.3;
  39. double d2 = 2.6;
  40. for (Operation2 o:Operation2.values()){
  41. System.out.printf("%f %s %f = %f%n",d1,o,d2,o.apply(d1,d2));
  42. }
  43. }
  44. }

输出结果

  1. 1.300000 + 2.600000 = 3.900000
  2. 1.300000 - 2.600000 = -1.300000
  3. 1.300000 * 2.600000 = 3.380000
  4. 1.300000 / 2.600000 = 0.500000

5)枚举类型有一个自动产生的valueOf(String)方法,它将常量的名字转变成常量本身。

  1. Operation2 plus = Operation2.valueOf("PLUS");
  2. System.out.println(plus);

输出结果

  1. +

如果在枚举类型中覆盖toString,要考虑编写一个fromString方法,将定制的字符串表示法变回相应的枚举,考虑用map保存

  1. private static Map<String,Operation3> operation3Map = new HashMap<>();
  2. static {
  3. for (Operation3 o:Operation3.values()){
  4. operation3Map.put(o.toString(),o);
  5. }
  6. }
  7. public static Operation3 fromString(String str){
  8. return operation3Map.get(str);
  9. }

测试方法

  1. Operation3 minus = Operation3.fromString("-");
  2. System.out.println(minus);

输出结果

  1. -

6)特定于常量的方法有一点不足,它们是的在枚举常量中共享代码变得困难

  1. public enum PayrollDay {
  2. MONDAY, TUESDAT, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
  3. /** * 正常工作时间 */
  4. private static final int HOURS_PER_SHIFT = 8;
  5. /** * 计算工资 * * @param hoursWorked 工作时间 * @param payRate 薪资 * @return 工资 */
  6. double pay(double hoursWorked, double payRate) {
  7. double basePay = hoursWorked * payRate;
  8. double overtimePay;
  9. switch (this) {
  10. case SATURDAY:
  11. case SUNDAY:
  12. overtimePay = hoursWorked * payRate / 2;
  13. default:
  14. overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;
  15. }
  16. return basePay + overtimePay;
  17. }
  18. }

当添加一个元素而没有添加相应的case时就会出现问题

可以使用枚举策略来解决,即讲加班工资的计算转移到一个私有的嵌套枚举中,将这个枚举的实例传到外部枚举的构造器中,这样外部枚举就可以将工资计算委托给内部枚举不需要再用switch进行转化了

  1. public enum PayrollDay2 {
  2. MONDAY(PayType.PAY_WEEKDAY),
  3. TUESDAY(PayType.PAY_WEEKDAY),
  4. WEDNESDAY(PayType.PAY_WEEKDAY),
  5. THURSDAY(PayType.PAY_WEEKDAY),
  6. FRIDAY(PayType.PAY_WEEKDAY),
  7. SATURDAY(PayType.PAY_WEEKEND),
  8. SUNDAY(PayType.PAY_WEEKEND);
  9. private final PayType payType;
  10. PayrollDay2(PayType payType) {
  11. this.payType = payType;
  12. }
  13. public double pay(double hoursWork, double payRate) {
  14. return this.payType.pay(hoursWork, payRate);
  15. }
  16. private enum PayType {
  17. PAY_WEEKDAY {
  18. @Override
  19. double overtimePay(double hoursWork, double payRate) {
  20. return hoursWork <= HOUR_SHIFT_TIME ? 0 : (hoursWork - HOUR_SHIFT_TIME) * payRate / 2;
  21. }
  22. },
  23. PAY_WEEKEND {
  24. @Override
  25. double overtimePay(double hoursWork, double payRate) {
  26. return hoursWork * payRate;
  27. }
  28. };
  29. private static final int HOUR_SHIFT_TIME = 8;
  30. abstract double overtimePay(double hoursWork, double payRate);
  31. public double pay(double hoursWork, double payRate) {
  32. double basePay = hoursWork * payRate;
  33. double overtimePay = overtimePay(hoursWork, payRate);
  34. return basePay + overtimePay;
  35. }
  36. }
  37. }

2.用实例域代替序数

  1. public enum Ensemble {
  2. SOLO(1),
  3. DUET(2),
  4. TRIO(3),
  5. QUARTET(4),
  6. QUEINTET(5),
  7. SEXTET(6),
  8. SEPTET(7);
  9. private final int numberOfMusicians;
  10. Ensemble(int size) {
  11. this.numberOfMusicians = size;
  12. }
  13. public int getNumberOfMusicians() {
  14. return numberOfMusicians;
  15. }
  16. //public int numberOfMusicians() {
  17. // //ordinal():返回每个枚举常量在类型中的数字位置
  18. // return ordinal() + 1;
  19. //}
  20. }

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

  1. public interface Operation {
  2. double apply(double x, double y);
  3. }
  4. public enum BaseOperation implements Operation {
  5. PLUS {
  6. @Override
  7. public double apply(double x, double y) {
  8. return x + y;
  9. }
  10. }, MINUS {
  11. @Override
  12. public double apply(double x, double y) {
  13. return x - y;
  14. }
  15. }, TIMES {
  16. @Override
  17. public double apply(double x, double y) {
  18. return x * y;
  19. }
  20. }, DIVIDE {
  21. @Override
  22. public double apply(double x, double y) {
  23. return x / y;
  24. }
  25. };
  26. }
  27. public enum ExtendOperation implements Operation {
  28. MOD {
  29. @Override
  30. public double apply(double x, double y) {
  31. return x % y;
  32. }
  33. },
  34. EXP {
  35. @Override
  36. public double apply(double x, double y) {
  37. return Math.pow(x, y);
  38. }
  39. };
  40. }

测试方法

  1. public class ExtendOperationTest {
  2. public static void main(String[] args) {
  3. double d1 = 1.3;
  4. double d2 = 2.6;
  5. test(ExtendOperation.class, d1, d2);
  6. test1(Arrays.asList(ExtendOperation.values()), d1, d2);
  7. }
  8. private static <T extends Enum<T> & Operation> void test(Class<T> opSet, double d1, double d2) {
  9. for (Operation o : opSet.getEnumConstants()) {
  10. System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));
  11. }
  12. }
  13. private static void test1(Collection<? extends Operation> opSet, double d1, double d2) {
  14. for (Operation o : opSet) {
  15. System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));
  16. }
  17. }
  18. }

输出结果

  1. 1.300000 MOD 2.600000 = 1.300000
  2. 1.300000 EXP 2.600000 = 1.978120
  3. 1.300000 MOD 2.600000 = 1.300000
  4. 1.300000 EXP 2.600000 = 1.978120

发表评论

表情:
评论列表 (有 0 条评论,316人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java

    ine, 我写这个是因为在读Calendar的时候发现对枚举类(enum)的使用不熟悉。 使用原因 * 清晰明了。例如枚举类SeasonEnum表示的季节,它...

    相关 Java SE学习总结

    本文是学习网络上的文章时的总结以及自己的一点实践,感谢大家无私的分享。 最近在看Java基础方面的书的时候,又遇到了枚举的介绍。在网上查了一些资料,对这些资料进行一些总结。

    相关 Java

    1.定义 enum 是一种数据类型,与 全局常量比较相似,都是全局的并且是可以通过类名调用的 与全局常量区别 枚举功能更强大,可以有属性和方法

    相关 学习笔记

    [2019独角兽企业重金招聘Python工程师标准>>> ][2019_Python_] ![hot3.png][] 枚举Enum > 关键字enum可以将一组具名的值