个人认为本章比较重要的有下面几个Item:
- Item 30: 使用enum代替int
- Item 32: 使用EnumSet 代替bit field
- Item 35: 注解优先于命名模式
其他几个Item可能学习不是那么深刻,并没深入记录。
本文原创, 原文地址:http://www.flyml.net/2017/03/09/effective-java-ch6-enum-annotation
Item 30: 使用enum代替int (Enum初步介绍)
虽然直译过来是"使用enum代替int", 但是这一小节实际上就是对enum的一个初步介绍
反例:
1 2 3 4 5 6 7 |
// The int enum pattern - severely deficient! public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2; |
上面这种方式非常不推荐, 比如命名空间是通过前缀,APPLE_
跟 ORANGE_
,几乎没有任何安全性可言。
比如, APPLE_GRANNY_SMITH = ORANGE_BLOOD
在编译跟运行的时候,不会有任何错误提示。 错误的排查也会很复杂。这种模式叫做int enum pattern
相应的, 可以将其改造成String enum pattern
(String模式),
1 2 3 4 5 6 |
public static final int APPLE_FUJI = "apple_fuji"; public static final int APPLE_PIPPIN = "applue_pippin"; public static final int APPLE_GRANNY_SMITH = "apple_granny_smith"; public static final int ORANGE_NAVEL = "orange_navel"; public static final int ORANGE_TEMPLE = "orange_temple"; public static final int ORANGE_BLOOD = "orange_blood"; |
虽然可以解决他们的值没有意义的问题, 但是性能上面会比较差,因为基础是字符串比较。同时依然不能比较命名空间的问题。
比较推荐的方法,使用enum:
1 2 |
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH } public enum Orange { NAVEL, TEMPLE, BLOOD } |
另外顺便提一下枚举类型的两个比较有用的特性:
- 支持默认值,并且默认值还可以是多个。比如:
1234567891011121314public enum Planet {MERCURY(1,2), EARTH(3,4);private final double mass; // In kilogramsprivate final double radius; // In metersprivate final double someOtherVal; // just for demoPlanet(double mass, double radius) {this.mass = mass;this.radius = radius;this.someOtherVal = 0;}}
注意: 这里的构造函数跟普通类的构造函数有点区别, 第7行的
someOtherVal
是一个测试的属性, 并且虽然不需要但是在初始化的时候(即构造函数之中)这也是很自然的事情, 因为枚举类型是不可变的类型
- 还可以定义相应的操作/函数
123456789101112131415161718192021222324252627282930public enum Operation {PLUS("+") {double apply(double x, double y) {return x + y;}},MINUS("-") {double apply(double x, double y) {return x - y;}},TIMES("*") {double apply(double x, double y) {return x * y;}},DIVIDE("/") {double apply(double x, double y) {return x / y;}};private final String symbol;Operation(String symbol) {this.symbol = symbol;}@Override public String toString() {return symbol;}abstract double apply(double x, double y);}
- 自带一些比较好用的方法
Planet.values()
这个方法自动获取了所有值valueOf()
从字符串转到相应的枚举类型的值123Mobile ret;ret = Mobile.valueOf("Samsung");System.out.println("Selected : " + ret);
- 策略枚举(Strategy Enum)这真心是一个比较复杂的例子。。。
123456789101112131415161718192021222324252627282930313233enum PayrollDay {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;PayrollDay(PayType payType) {this.payType = payType;}double pay(double hoursWorked, double payRate) {return payType.pay(hoursWorked, payRate);}// The strategy enum typeprivate enum PayType {WEEKDAY {double overtimePay(double hours, double payRate) {return hours <= HOURS_PER_SHIFT ? 0 :(hours - HOURS_PER_SHIFT) * payRate / 2;}},WEEKEND {double overtimePay(double hours, double payRate) {return hours * payRate / 2;}};private static final int HOURS_PER_SHIFT = 8;abstract double overtimePay(double hrs, double payRate);double pay(double hoursWorked, double payRate) {double basePay = hoursWorked * payRate;return basePay + overtimePay(hoursWorked, payRate);}}}
这是在一个
Enum
之中又嵌套了一个Enum
本文原创, 原文地址:http://www.flyml.net/2017/03/09/effective-java-ch6-enum-annotation
Item 31: Use instance fields instead of ordinals
在Enum 之中有一个自带的方法ordinal()
, 返回这个值的顺序。
但是非常不推荐使用这个值作为值默认的值。 宁愿麻烦一点人工写出来,否则后期的维护会很痛苦, 因为谁也不知道会不会调整Enum之中的值的顺序。
Item 32 : 使用EnumSet 代替bit field
很多时候, 我们在JDK或者C++之中都能看到类似下面的做法:
1 2 3 4 5 6 7 8 9 |
// Bit field enumeration constants - OBSOLETE! public class Text { public static final int STYLE_BOLD = 1 << 0; // 1 public static final int STYLE_ITALIC = 1 << 1; // 2 public static final int STYLE_UNDERLINE = 1 << 2; // 4 public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8 // Parameter is bitwise OR of zero or more STYLE_ constants public void applyStyles(int styles) { ... } } |
使用的时候,就像下面这样:
1 |
text.applyStyles(STYLE_BOLD | STYLE_ITALIC); |
即: 通过位运算的方式来进行操作。
这种方法看起来比较高级, 问题也不是特别大, 但是灵活性还是不够好。 比较推荐使用EnumSet
的方式来代替, 参考下面的代码:
1 2 3 4 5 6 |
// EnumSet - a modern replacement for bit fields public class Text { public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } // Any Set could be passed in, but EnumSet is clearly best public void applyStyles(Set<Style> styles) { ... } } |
注意: 上面的applyStyles()
方法的参数变成了一个Set
。这样操作起来就清晰很多了~
在调用的时候, 也很清晰:
1 |
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); |
EnumSet
是JDK自带的一个类。
本文原创, 原文地址:http://www.flyml.net/2017/03/09/effective-java-ch6-enum-annotation
Item 33 : 使用EnumMap 代替ordinal值
PS: 脑海之中回顾了一下, 感觉目前并没有太多的使用EnumMap的场景。 贴一个看起来更容易懂的使用示例:
参考自:http://www.cnblogs.com/chenpi/archive/2016/03/19/5296330.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.pichen.collection; import java.util.EnumMap; import java.util.Map; import java.util.Map.Entry; enum Operate{ ADD, UPDATE, DELETE; } public class Main { public static void main(String[] args) { Map<Operate, String> map = new EnumMap<Operate, String>(Operate.class); //put方法 map.put(Operate.ADD, "add operate"); map.put(Operate.UPDATE, "update operate"); map.put(Operate.DELETE, "delete operate"); //重写了toString方法 System.out.println(map); //size方法 System.out.println(map.size()); System.out.println(map.containsKey(Operate.UPDATE)); System.out.println(map.containsValue("update operate")); System.out.println(map.get(Operate.DELETE)); //remove map.remove(Operate.UPDATE); System.out.println(map); //key集合 for(Operate operate:map.keySet()){ System.out.print(operate + " "); } System.out.println(); //value集合 for(String obj:map.values()){ System.out.print(obj + ","); } System.out.println(); //key-value集合 for(Entry<Operate, String> entry:map.entrySet()){ System.out.print(entry.getKey() + ": " + entry.getValue() + ", "); } } } |
Item 35: 注解优先于命名模式
命名模式一个典型的应用就是在JUnit之中, 如果方法以 test
开头就属于单元测试。这种模式, 如果一不小心写错了字, 要发现就很麻烦,维护起来也很危险。
比较推荐的方法是使用注解, 即增加一个 @Test
的注解,告诉Junit这是一个单元测试方法。
其他的没有细细看懂。。。
Item 36: 坚持使用Override
注解
记住就好, 本身并没有太多要说的。。。
本文原创, 原文地址:http://www.flyml.net/2017/03/09/effective-java-ch6-enum-annotation
文章评论