Effective Java 学习笔记之枚举

来源:互联网 发布:php高级视频教程下载 编辑:程序博客网 时间:2024/06/05 00:12

1.用enum代替int常量

1)为了将数据和枚举常量关联起来 得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。
2)枚举天生就是不可变的,因此所有的域都应该是final的。它们可以是公有的,但是最好做成私有并提供公有的访问方式
3)枚举类型还允许添加任意的方法和域 并实现任意的接口
4)提供了所有Object方法的高级实现 实现了Comparable和Serializable接口,并设计了序列化方式

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;    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 getMass() {        return mass;    }    public double getRadius() {        return radius;    }    public double getSurfaceGravity() {        return surfaceGravity;    }    public double getSurfaceWeight(double mass) {        return mass * surfaceGravity;    }}

扩展:需要为每个常量添加不同的行为

public enum Operation1 {    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("Unknown op:" + this);    }}

上面的代码存在一些问题,当为枚举类添加新的常量后,忘记为其提供相应apply的实现,编译不会出现错误提示,然而运行结果却不是希望的

因此可以进行修改

提供一个抽象的apply方法,这样每个常量都必须实现这个抽象方法,否则编译就会报错

    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);

有时候复写toString方法也会非常有用

public enum Operation2 {    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);    private String symbol;    Operation2(String symbol) {        this.symbol = symbol;    }    @Override    public String toString() {        return symbol;    }}
public class OpeartionTest {    public static void main(String[] args) {        double d1 = 1.3;        double d2 = 2.6;        for (Operation2 o:Operation2.values()){            System.out.printf("%f %s %f = %f%n",d1,o,d2,o.apply(d1,d2));        }    }}

输出结果

1.300000 + 2.600000 = 3.9000001.300000 - 2.600000 = -1.3000001.300000 * 2.600000 = 3.3800001.300000 / 2.600000 = 0.500000

5)枚举类型有一个自动产生的valueOf(String)方法,它将常量的名字转变成常量本身。

Operation2 plus = Operation2.valueOf("PLUS");System.out.println(plus);

输出结果

+

如果在枚举类型中覆盖toString,要考虑编写一个fromString方法,将定制的字符串表示法变回相应的枚举,考虑用map保存

private static Map<String,Operation3> operation3Map = new HashMap<>();static {   for (Operation3 o:Operation3.values()){       operation3Map.put(o.toString(),o);   }}public static Operation3 fromString(String str){    return operation3Map.get(str);}

测试方法

Operation3 minus = Operation3.fromString("-");System.out.println(minus);

输出结果

-

6)特定于常量的方法有一点不足,它们是的在枚举常量中共享代码变得困难

public enum PayrollDay {    MONDAY, TUESDAT, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;    /**     * 正常工作时间     */    private static final int HOURS_PER_SHIFT = 8;    /**     * 计算工资     *     * @param hoursWorked 工作时间     * @param payRate     薪资     * @return 工资     */    double pay(double hoursWorked, double payRate) {        double basePay = hoursWorked * payRate;        double overtimePay;        switch (this) {            case SATURDAY:            case SUNDAY:                overtimePay = hoursWorked * payRate / 2;            default:                overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;        }        return basePay + overtimePay;    }}

当添加一个元素而没有添加相应的case时就会出现问题

可以使用枚举策略来解决,即讲加班工资的计算转移到一个私有的嵌套枚举中,将这个枚举的实例传到外部枚举的构造器中,这样外部枚举就可以将工资计算委托给内部枚举不需要再用switch进行转化了

public enum PayrollDay2 {    MONDAY(PayType.PAY_WEEKDAY),    TUESDAY(PayType.PAY_WEEKDAY),    WEDNESDAY(PayType.PAY_WEEKDAY),    THURSDAY(PayType.PAY_WEEKDAY),    FRIDAY(PayType.PAY_WEEKDAY),    SATURDAY(PayType.PAY_WEEKEND),    SUNDAY(PayType.PAY_WEEKEND);    private final PayType payType;    PayrollDay2(PayType payType) {        this.payType = payType;    }    public double pay(double hoursWork, double payRate) {        return this.payType.pay(hoursWork, payRate);    }    private enum PayType {        PAY_WEEKDAY {            @Override            double overtimePay(double hoursWork, double payRate) {                return hoursWork <= HOUR_SHIFT_TIME ? 0 : (hoursWork - HOUR_SHIFT_TIME) * payRate / 2;            }        },        PAY_WEEKEND {            @Override            double overtimePay(double hoursWork, double payRate) {                return hoursWork * payRate;            }        };        private static final int HOUR_SHIFT_TIME = 8;        abstract double overtimePay(double hoursWork, double payRate);        public double pay(double hoursWork, double payRate) {            double basePay = hoursWork * payRate;            double overtimePay = overtimePay(hoursWork, payRate);            return basePay + overtimePay;        }    }}

2.用实例域代替序数

public enum Ensemble {    SOLO(1),    DUET(2),    TRIO(3),    QUARTET(4),    QUEINTET(5),    SEXTET(6),    SEPTET(7);    private final int numberOfMusicians;    Ensemble(int size) {        this.numberOfMusicians = size;    }    public int getNumberOfMusicians() {        return numberOfMusicians;    }    //public int numberOfMusicians() {    //    //ordinal():返回每个枚举常量在类型中的数字位置    //    return ordinal() + 1;    //}}

3.用接口模拟可伸缩的枚举

public interface Operation {    double apply(double x, double y);}public enum BaseOperation implements Operation {    PLUS {        @Override        public double apply(double x, double y) {            return x + y;        }    }, MINUS {        @Override        public double apply(double x, double y) {            return x - y;        }    }, TIMES {        @Override        public double apply(double x, double y) {            return x * y;        }    }, DIVIDE {        @Override        public double apply(double x, double y) {            return x / y;        }    };}public enum ExtendOperation implements Operation {    MOD {        @Override        public double apply(double x, double y) {            return x % y;        }    },    EXP {        @Override        public double apply(double x, double y) {            return Math.pow(x, y);        }    };}

测试方法

public class ExtendOperationTest {    public static void main(String[] args) {        double d1 = 1.3;        double d2 = 2.6;        test(ExtendOperation.class, d1, d2);        test1(Arrays.asList(ExtendOperation.values()), d1, d2);    }    private static <T extends Enum<T> & Operation> void test(Class<T> opSet, double d1, double d2) {        for (Operation o : opSet.getEnumConstants()) {            System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));        }    }    private static void test1(Collection<? extends Operation> opSet, double d1, double d2) {        for (Operation o : opSet) {            System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));        }    }}

输出结果

1.300000 MOD 2.600000 = 1.3000001.300000 EXP 2.600000 = 1.9781201.300000 MOD 2.600000 = 1.3000001.300000 EXP 2.600000 = 1.978120

原创粉丝点击