Java 1.5发行版本新增了两个引用类型家族:枚举类型(Enumerate类)和注解类型(Annotation接口)。
第三十条、用enum代替int常量
-
枚举类型是指由一组固定的常量组成合法值的类型。替代之前的具名的int常量。
public static final int APPLE_FUJI = 0;
这种方式称作int枚举模式,存在诸多的不足:
程序十分脆弱,因为int枚举是编译时常量,被编译到使用它们的客户端中。如果与枚举常量关联的int发生了变化,客户端就必须重新编译,否则,程序行为会变得不确定性。(还有个变体是String枚举模式) -
java的枚举类型是功能十分齐全的类,本质上是int值。
基本想法是:通过公有的静态final域为每个枚举常量导出实例的类。枚举类型是实例受控的,它们是单例的泛型化,本质上是单元素的枚举。枚举提供了编译时的类型安全,如果声明一个参数的类型为Apple,就可以保证,被传到该参数上的任何非null的对象引用一定属于三个有效的Apple值之一。试图传递类型错误的值时,会导致编译时错误。
public enum Apple{FUJI,PIPPIN,GERNNY_SMITH}
包含同名变量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间。 -
除了弥补int枚举常量的不足,枚举类型还允许添加任意的方法和域(近似于类),并实现任意的接口。它们提供了所有Object方法的高级实现,实现了Comparable和Serializable接口。我们为啥要将方法或者域加入到枚举类型中?将数据与它的常量关联起来。可以用任何适当的方法来增强枚举类型。
/** * Created by laneruan on 2017/7/10. * 一个枚举类型的例子 * 为了将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。 */ public class EnumPlanet { public enum Planet{ MERCURY(3.302e+23,2.439e6), VENUS(4.8693+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; private final double radius; private final double surfaceGravity; private static final double G = 6.67300E-11; 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; } } public static void main(String[] args){ double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight/Planet.EARTH.surfaceGravity(); for (Planet p : Planet.values()){ System.out.println("Weight on " + p +" is "+p.surfaceWeight(mass)); } } }
与枚举常量关联的有些行为,可能只需要用在定义了枚举的类或者包中,这种行为最好被实现成私有的或者包级私有的方法。
如果一个枚举具有普遍适用性,它就应该成为一个顶层类。如果它是被用在一个特定的顶层类中,它就应该成为该顶层类的一个成员类。 -
特定于常量的方法实现:将不同的行为与每个枚举常量关联起来。
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), }
什么时候应该使用枚举类型?
每当需要一组固定常量的时候。包括:天然的枚举类型;在编译的时候就知道其所有可能值的集合。总结:与int常量相比,枚举类型的优势不言而喻:易读,更加安全。功能更加强大。许多枚举都不需要显式的构造器或者成员,但许多其他枚举类型则受益于“每个常量与属性的关联”以及“提供行为受这个属性影响的方法”。
第三十一条、用实例域代替序数
许多枚举天生就与一个单独的int值相关联,所有的枚举都有一个ordinal方法,它返回每个枚举常量在类型中的数字位置。可以试着从序数中得到关联的int值。最好避免使用ordinal方法。
永远不要根据枚举的序数导出与它想关联的值,而是要将它保存在一个实例域中:
public enum Ensemble{
SOLO(1),DUET(2),TRIO(3),QUARTET(4),QUINTET(5),
SETET(6),SEPET(7),OCTET(8),NONET(9),DECTET(10);
private final int numberOfMusicians;
Ensemble(int size){
this.numberOfMusicians = size;
}
public int numberOfMusicians(){return numberOfMusicians;}
// public int numberOfMusicians(){return ordinal()+1;}
}
第三十二条、用EnumSet代替位域
需要传递多组常量集时,java.util包提供了EnumSet类来有效地表示从单个枚举类型中提取多个值的多个集合,这个类实现了Set接口,提供了丰富的功能。
class Text{
public enum Style {BOLD,ITALIC,UNDERLINE,STRIKETHROUGH}
public void applyStyles(Set