第六章 枚举和注解_effective_java

第三十条:用enum代替int常量

相比于声明成final static int 或string,使用枚举是更好的选择:

  • int的缺点:无法打印出有效值,无法保证一个变量的int值一定有效(在你列出的备选值中)
  • string:基于字符串比较带来性能问题;无法在编译时杜绝拼写错误等问题。
  • enum的好处。
    • 是单例的泛型。
    • 保证被传到的参数一定是有效值。
    • 包含同名常量的枚举类型可以在一个系统中共存

枚举可以声明方法和域,用这些方法可以将枚举从一个简单的集合变成一个全功能的抽象,比如这个行星的例子:

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
public enum Planet {
MERCURY(3.302e+23, 2.439e6), VENUS(4.869e+24, 6.052e6), EARTH(5.975e+24,
6.378e6), MARS(6.419e+23, 3.393e6), JUPITER(1.899e+27, 7.149e7), SATURN(
5.685e+26, 6.027e7), URANUS(8.683e+25, 2.556e7), NEPTUNE(1.024e+26,
2.477e7);
private final double mass; // In kilograms
private final double radius; // In meters
private final double surfaceGravity; // In m / s^2
// Universal gravitational constant in m^3 / kg s^2
private static final double G = 6.67300E-11;
// Constructor
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double mass() {return mass;}
public double radius() {return radius;}
public double surfaceGravity() {return surfaceGravity; }
public double surfaceWeight(double mass) { return mass * surfaceGravity; /** F = ma**/ }
}

通过设定mass,radius和常量G,枚举可以调用方法返回自己的各种信息。这里所有域都应该是final的,而且尽量是私有的。

枚举中有个使用的函数values(),对一个枚举调用values()可以按照声明顺序返回他的值的数组,比如下面这个函数会答应Planet所有的值的名称:

1
2
3
4
public void printALL(){
for (Planet p: Planet.values())
System.out.print(p.toString());
}

枚举的toString如果你不重载他,默认会返回这个枚举值的名字,比如MERCURY。

枚举还有两个需要注意的方法

  • oridinal(),他返回该枚举值在枚举中的位置,比如Planet.MERCURY.oridinal(),返回0。
  • valueOf(String),他将常量的名字变为枚举值。比如Planet.valueOf("MERCURY")返回Planet.MERCURY对象

枚举可以定义抽象方法,定义后每个枚举值都需要实现该方法,这有助于帮助你实现不同常量的特定逻辑而不至于被遗忘,比如:

1
2
3
4
5
6
7
public 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;}
abstract double apply(double x, double y);
};

第三十一条:用实例域代替序数 && 第三十三条 用EnumMap代替序数索引

虽然有oridinal()函数,但是并不建议使用它,因为他严重限制了程序的扩展性,新加一个枚举值在这种情况下非常危险。如果有相关需求最好在enum里面加一个域在存放相关值。

同样的,当你需要索引Enum时,建议使用EnumMap而不是序数

第三十二条:用EnumSet代替位域

有时候为了方便的求交集,程序员会写A=1<<0,B=1<<1这种,在判断时直接按位或。这种需求可以将常量AB声明成Enum,然后用EnumSet实现。

第三十四条:用接口模拟可伸缩的枚举类

因为枚举不能继承,所以无法有子枚举类和超枚举类,所以有这种扩展的需求可以让enum 实现接口,然后调用接口来模拟这种情况。

第三十五条: 注解优先于命名模式

命名模式是那种比如限定你的函数以test开头,然后会自动执行test开头的所有函数的方法,这个方法有一个问题就是假如你的test拼成了tset,在编译和运行时都不会报错,只是你这段程序永远也不会执行,从而导致错误的结果。

注解可以很好的解决这个问题。注解的使用可以参考文档1文档2是对其中关键字@Inherited的说明

第三十六条:坚持使用Override注解

override可有有效避免覆盖父类函数时错误的写成了重载的问题,有助于编译器帮你发现bug。


本文采用创作共用保留署名-非商业-禁止演绎4.0国际许可证,欢迎转载,但转载请注明来自http://thousandhu.github.io,并保持转载后文章内容的完整。本人保留所有版权相关权利。

本文链接:http://thousandhu.github.io/2016/10/18/第六章-枚举和注解-effective-java/