枚举和注解

来源:互联网 发布:图片浏览软件下载 编辑:程序博客网 时间:2024/06/03 17:47
30.用enum代替int类型

枚举类型指由一组固定的常量组成合法值的类型,没有枚举型前用的是声明一组常量。在安全和使用上没有帮助,将Apple传到orange也没有问题,还会用==比较,而且是编译器常量,如果改变,必须重新编译,不重新编译也没问题,行为就不确定了

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 PRANGE_NAVEL = 0;public static final int ORANGE_TEMPLE = 1;public static final int ORANGE_BLOOD = 2;

这是int枚举模式

public enum Apple{FUJI,PIPPIN,GRANNY_SMITH}public enum Orange{NAVEL,TEMPLE,BLOOD}
本质是int型,是通过公有的静态final域为每个枚举常量导出实例的类,没有可以访问的构造器,枚举类型是真正的final,是单例和泛型化的
public enum Planet {//枚举常量后面括号中的数值就是传递给构造器的参数MERCURY(3.302e+23,2.439e6),VENUS(4.859e+24,2.439e6),EARTH(3.302e+23,2.439e6),MARS(3.302e+23,2.439e6),JUPITER(3.302e+23,2.439e6),SATUNE(3.302e+23,2.439e6),URANUS(3.302e+23,2.439e6),NEPTUNE(3.302e+23,2.439e6);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 class WeightTable {public static void main(String[] args) {double earthWeight = 1;double mass = earthWeight /Planet.EARTH.surfaceGravity();//静态的values方法,按照声明顺序返回值数组for (Planet planet : Planet.values()) {System.out.println(planet.surfaceWeight(mass));}}}
//计算加减乘除,通过启用枚举的值来实现,代码脆弱,如果添加新的枚举值,switch里不添加相应的条件,试图用新的运算 ,会运行失败public enum Operation {PLUS,MINUS,TIMES,DIVIDE;double apply(double x,double y){switch (this) {case PLUS:return x+y;case MINUS:return x-y;case TIMES:return x*y;case DIVIDE:return x/y;}throw new AssertionError();}}
//将不同的行为与每个常量连接起来,在枚举类型中声明一个抽象方法,在常量的类主体中,用具体的方法覆盖每个常量的抽象apply方法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);}
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;}};private final String symbol;private Operation(String symbol) {this.symbol=symbol;}//覆盖toString方法返回与该操作相关联的符号@Overridepublic String toString(){return symbol;}abstract double apply(double x,double y);}
//常量被创建后,Operation常量从静态代码块中被放入到stringToEnum的map中private static final Map<String, Operation> stringToEnum = new HashMap<>();static{for (Operation op:values()) {stringToEnum.put(op.toString(), op);}}//将定制的字符串表示法变回相应的枚举public static Operation fromString(String symbol){return stringToEnum.get(symbol);}
每添加一个枚举常量,强制选择一种策略,策略枚举

//将加班工资计算移到一个私有的嵌套枚举中enum PayrollDay {MONDAY(PayType.WEEKDAY),TUESDAY(PayType.WEEKDAY),WENESDAY(PayType.WEEKDAY),THURSDAY(PayType.WEEKDAY),FRIDAY(PayType.WEEKDAY),SATURDAY(PayType.WEEKEND),SUNDAY(PayType.WEEKEND);private final PayType payType;PayrollDay(PayType payType) {this.payType =payType;}private 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 hours,double payRate);double pay(double hoursWorked,double payRate){double basePay = hoursWorked * payRate;return basePay + overTimePay(hoursWorked, payRate);}}}
枚举中的switch语句适合于给外部的枚举类型增加特定于常量的行为。
//假设Operation枚举不受自己的控制,希望它有个实例方法来返回每个运算的反运算public static Operation inverse(Operation op){switch (op) {case PLUS:return Operation.MINUS;case MINUS:return Operation.PLUS;case TIMES:return Operation.DIVIDE;case DIVIDE:return Operation.MINUS;default:throw new AssertionError(op);}
每当需要一组固定常量的时候使用枚举,包括在编译时就知道的所有可能值的其他集合
31.用实例域代替序数

所有的枚举都有一个ordinal方法,它返回每个枚举常量在类型中的数字位置

public enum Ensenble {SOLO,DUET,TRIO,QUARTET,QUINTET,SEXTET,SEPTET,OCTET,NONET,DECTET;public int numberOfMusicians(){return ordinal()+1;}}
常量进行重新排序,numberOfMusicians方法就会遭到破坏,再添加一个已经用过的int值关联的枚举常量,无法做到,要是没有给所有这些int值添加常量,也无法给某个int值添加常量

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

public enum Ensenble {SOLO(1),DUET(2),TRIO(3),QUARTET(4),QUINTET(5),SEXTET(6),SEPTET(7),OCTET(8),NONET(9),DECTET(10);private final int numberOfMusicians;private Ensenble(int size) {this.numberOfMusicians = size;}public int numberOfMusicians(){return numberOfMusicians;}}
32.用EnumSet代替位域
public class Text {public static final int STYLE_BOLD = 1 << 0;public static final int STYLE_ITALIC = 1 <<1;public static final int STYLE_UNDERLINE = 1 <<2;public static final int STYLE_STRIKETHROUGH = 1 <<3;public void applyStyles(int styles){}}
//这种做法用or运算符将几个常量合并到一个集合中,称为位域text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
用枚举代替位域的做法
public class Text {public enum Style {BOLD,ITALIC,UNDERLINE,STRIKETHROUGH};public void applyStyles(Set<Style> styles){}}
text.applyStyles(EnumSet.of(Style.BOLD,Style.ITALIC));
33.用EnumMap代替序数索引

简化的类

public class Herb {public enum Type{ANNUAL,PERENNIAL,BIENNIAL};private final String name;private final Type type;Herb(String name, Type type) {this.name = name;this.type = type;}@Overridepublic String toString() {return name;}}
分别组织植物,按照年份来分,把分的集合放到按照类型的序数进行索引的数组中来实现,会有问题,数组与泛型不兼容,要进行未受检的转换,不能正确无误的编译,访问按照枚举的序数进行索引的数组时,使用错误的值,程序会完成错误的工作,或报数组下标越界错误

可以用EnumMap来做

public class Test {public static void main(String[] args) {Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<>(Herb.Type.class);for(Herb.Type t:Herb.Type.values()){herbsByType.put(t, new HashSet<Herb>());}for(Herb h:garden){herbsByType.get(h.type).add(h);}System.out.println(herbsByType);}}
34.用接口模拟可伸缩的枚举
枚举类型不可拓展,但接口可以拓展
public interface Operation {double apply(double x,double y);}public enum BasicOperation implements Operation{PLUS("+"){public double apply(double x,double y){return x+y;}},MINUS("-"){public double apply(double x,double y){return x-y;}},TIMES("*"){public double apply(double x,double y){return x*y;}},DIVIDE("/"){public double apply(double x,double y){return x/y;}};private final String symbol;private BasicOperation(String symbol) {this.symbol = symbol;}//覆盖toString方法返回与该操作相关联的符号@Overridepublic String toString(){return symbol;}}

实现Operation接口

public enum ExtendedOperation implements Operation{EXP("^"){public double apply(double x,double y){return Math.pow(x, y);}},REMAINDER("%"){public double apply(double x,double y){return x%y;}},private final String symbol;private ExtendedOperation(String symbol) {this.symbol = symbol;}@Overridepublic String toString(){return symbol;}}


 

public class Test {public static void main(String[] args) {double x = Double.parseDouble("11");double y = Double.parseDouble("22");test(ExtendedOperation.class,x,y);}private static <T extends Enum<T> &Operation> void  test(Class<T> opSet,double x,double y) {for (Operation op:opSet.getEnumConstants()) {System.out.printf("%f %s %f = %f%n",x,op,y,op.apply(x, y));}};}

<T extends Enum<T> &Operation>确保了class对象即表示枚举又表示Operation的子类型,是遍历元素和执行每个元素相关联的操作时所需要的

public class Test {public static void main(String[] args) {double x = Double.parseDouble("11");double y = Double.parseDouble("22");test(Arrays.asList(ExtendedOperation.values()),x,y);}private static  void  test(Collection<? extends Op7>,double x,double y) {for (Operation op:opSet.getEnumConstants()) {System.out.printf("%f %s %f = %f%n",x,op,y,op.apply(x, y));}};}

不足点是无法将实现从一个枚举类型继承到另一个枚举类型,保存和获取与某项操作相关联的符号的逻辑代码可以复制到实现类上,如果共享内容过多,可以把它封装到辅助类或者静态辅助方法中,避免代码的复制工作