回头再写备注
1、枚举类就是class并且是一个不可被继承的final class,其枚举值都是public static final的。
2、既然枚举类是class其就会有构造、方法、数据域。但是枚举类的构造器有很大不同:首先,构造器只是在构造枚举值时使用;其次,构造器是private的,不允许为public(这样可以保证外部代码无法新构造枚举类的实例。这也是完全符合情理的,因为我们知道枚举值是public static final的常量而已。 但枚举类的方法和数据域可以允许外部访问)。
3、所有枚举类都继承了Enum的方法,下面我们详细介绍这些方法。 (1) ordinal()方法: 返回枚举值在枚举类种的顺序。这个顺序根据枚举值声明的顺序而定。 (2) compareTo()方法: Enum实现了java.lang.Comparable接口,因此可以比较象与指定对象的顺序。Enum中的compareTo返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出ClassCastException()异常。 (3) values()方法: 静态方法,返回一个包含全部枚举值的数组。 (4) toString()方法: 返回枚举常量的名称。 (5) valueOf()方法: 这个方法和toString方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。 (6) equals()方法: 比较两个枚举类对象的引用。
4、枚举类可以在switch语句中使用。
5.特定于常量的枚举方法
6.策略枚举(strategy enum)
package cn.com.imooc;//Enum type with data and behaviorpublic enum Planet { MERCURY(3.302e+23,2.439e6), VENUS(4.869E+24,6.052e6), EARCH(5.975e+24,6.378e6), MARS (6.419e+23,3.393e6), JUPITER(1.899e+27,7.149e7), STATURN(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 }}
测试:
package cn.com.test;import cn.com.imooc.Planet;public class WeightTable { public static void main(String[] args) { double earthWeight = Double.parseDouble("175"); double mass = earthWeight/Planet.EARCH.surfaceGravity(); for(Planet p : Planet.values()){ System.out.printf("Weight on %s is %f%n",p,p.surfaceWeight(mass)); } }}
枚举类实现最基本的加减乘除:
package cn.com.imooc;//Enum type that switches on its own value -questionable/** * 枚举类实现最基本的加减乘除 * @author xiaomingtongxie * */public enum Operation { PLUS,MINUS,TIMES,DIVIDE; //Do the arithmetic op represented by this constant 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("Unkonwn op:" + this); }}
枚举类型是编译安全类型,加入增加了新的运算枚举类,而没有在switch 中添加对应的条件,编译时不会报错,但运行时会出错。
于是有了,特定于常量的方法实现,优化代码如下:
package cn.com.imooc;//Enum type with constant-specific method implementationspublic 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);}
枚举类中的抽象方法必须被它所有常量中的具体方法所覆盖,这样就不存在有编译时错误了。
package cn.com.imooc;//Enum type with constant-specific method implementationspublic 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);}
测试:
public static void main(String[] args) { //Operation obj = Operation.MINUS; double x = Double.parseDouble("2"); double y = Double.parseDouble("4"); for (Operation obj : Operation.values()) { System.out.printf("%f %s %f = %f%n",x,obj,y,obj.apply(x, y)); } }
在枚举类中覆盖toString,要考虑编写一个fromString方法,见定制的字符串表示法变回相应的枚举。
//Implementing a fromString method on an enum type private static final MapstringToEnum = new HashMap (); static{ // Initialize map from constant name to enum constant for (Operation obj : values()) { stringToEnum.put(obj.toString(), obj); } } //Return Operation for string, or null if string is invalid public static Operation fromString(String symbol) { return stringToEnum.get(symbol); }
试图使每个常量都从自己的构造器将自身放入到map中,会导致编译时错误。枚举构造器不可以访问枚举的静态域,除了编译时常量域除外。
//实现工作日正常工资,双休加班工资是平时一半
package cn.com.imooc;//Enum tha switches on its value to share code-questionablepublic enum PayrollDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; private static final int HOURS_PER_SHIFT = 8; double pay(double hourWorked, double payRate) { double basePay = hourWorked * payRate; double overtimePay; // Calculate overtime pay switch (this) { case SATURDAY: case SUNDAY: overtimePay = hourWorked * payRate / 2; default: // Weekdays overtimePay = hourWorked <= HOURS_PER_SHIFT ? 0 : (hourWorked * payRate / 2); break; } return basePay + overtimePay; }}
但是,如果添加了个特殊的枚举,代表假期天数的特殊值,但忘记添加相应的case.程序依然可以编译,但pay方法会悄悄地将假期的工资算成域常量工作日的相同。
每当添加一个枚举常量时,就强制选择一种加班报酬策略。
下面就是策略枚举的实现:
package cn.com.imooc;//The strategy enum patternpublic enum 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 type 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 hrs, double payRate); double pay(double hoursWorked, double payRate) { double basePay = hoursWorked * payRate; return basePay + overtimePay(hoursWorked, payRate); } }}
这种思想就是将加班工资计算一道一个私有的嵌套枚举中,将这个策略枚举的实例转到PayrollDay枚举的构造器中。
当然,switch 还是有用的,比如说加减法倒置,乘除法倒置
//Switch on an enum to simulate a missing method 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.TIMES; default: throw new AssertionError("Unkown op:" + op); } }