• 第30条:用enum代替int常量


    在java1.5之前,表示枚举类型的常用模式是声明一组具名的int常量,每个类型成员一个常量:

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

    缺点:

    1.将apple传到想要orange的方法中,不会出现错误

    2.用==操作符将apple与orange比较,不会出现错误

    3.int枚举是编译时常量,被编译到客户端中,如果枚举常量关联的int发生变化,客户端必须重新编译,如果没有重新编译,程序仍可以运行,但行为就确定了,如APPLE_FUJI关联的常量不再是0,而是3。

    4.将int枚举常量翻译成可打印的字符串很麻烦

    5.遍历一个组中所有的int枚举常量,获得int枚举组的大小,没有可靠的方法,如想知道APPLE的常量有多少个,除了查看int枚举常量所在位置的代码外,别无他法,而且靠的是观察APPLE_前缀有多少个,不可靠

    枚举类型是int枚举常量的替代解决方案:

    public enum Apple {FUJI, PIPPIN, GRANNY_SMITH}
    public enum Orange {NAVEL, TEMPLE, BLOOD}

    枚举是功能齐全的类,通过公有的静态final域为每个枚举常量导出实例的域。因为没有可以可以访问的构造器,客户端不能创建枚举类型的实例,也不能扩展它。

    枚举提供编译时的类型安全,如果一个参数的类型是Apple,就可以保证,被传入到该参数上的任何非null对象引用一定是FUJI,PIPPIN,GRANNY_SMITH三个之一。

    包含同名常量的多个枚举类型可以共存,因为每个类型有自己的命名空间,增加或重新排列枚举类型的常量,无需重新编译客户端代码。

    通过调用toString方法,可以将枚举转换成可打印的字符串。

    枚举类型有方法和域:

    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 mass() {
            return mass;
        }
        
        public double radius() {
            return radius;
        }
        
        public double surfaceGravity() {
            return surfaceGravity;
        }
        
        public double surfaceWeight(double mass) {//F=ma
            return mass * surfaceGravity;
        }
        
    }

    根据某个物体在地球上的重量,显示该物体在所有8颗行星上的重量:

    public static void main(String[] args) {
            double earthWeight = 175;
            double mass = earthWeight / Planet.EARTH.surfaceGravity();
            for(Planet p : Planet.values()) {
                System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass));
            }
    }

    如果枚举具有普遍适用性,它应该成为一个顶层类,如果只被用在一个特定的顶层类中,应该成为顶层类的一个成员类。

    java.math.RoundingMode枚举表示十进制的舍入模式,用在BigDecimal类。

    Planet示例展示每个常量关联不同的数据,有时候需要将本质上不同的行为与常量关联起来。

    假设有一个枚举类型,表示计算器的四大基本操作:

    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;
        Operation(String symbol) {
            this.symbol = symbol;
        }
        public String toString() {
            return symbol;
        }
        abstract double apply(double x, double y);
    }

    某些情况下,覆盖toString方法使打印算术表达式很容易:

    public static void main(String[] args) {
        double x = 2.0;
        double y = 4.0;
        for(Operation op : Operation.values())
            System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x,y));
    }

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

    如果覆盖toString方法,要考虑编写一个fromString方法,将字符串表示法变回相应的枚举:

    private static final Map<String, Operation> stringToEnum
        = new HashMap<String, Operation>();
    static {
        for(Operation op : values())
            stringToEnum.put(op.toString(), op);
    }
    public static Operation fromString(String symbol) {
        return stringToEnum.get(symbol);
    }

    枚举策略解决多个枚举常量同时共享相同的行为。

    加班报酬,weekday和weekend的计算方式不一样:

    enum PayrollDay {
        MONDAY(PayType.WEEKDAY),
        THESDAY(PayType.WEEKDAY),
        WEDNESDAY(PayType.WEEKDAY),
        THURSDAY(PayType.WEEKDAY),
        FRIDAY(PayType.WEEKDAY),
        SATURDAY(PayType.WEEKEND),
        SUNDAY(PayType.WEEKEND);
        
        private final PayType p;
        PayrollDay(PayType p) {
            this.p = p;
        }
        double pay(double hoursWorked, double payRate) {
            return payType.pay(hoursWorked, payRate);
        }
    
        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 final static int HOURS_PER_SHIFT = 8;
            abstract double overtimePay(double hours, double Rate);
            double pay(double hoursWorked, double payRate) {
                double basePay = hoursWorked * payRate;
                return basePay + overtimePay(hoursWorked, payRate);
            }
        }
    }

    每当需要一组固定常量的时候,就应该使用枚举。

  • 相关阅读:
    Java基础知识回顾
    设计模式简单回顾
    数据结构基础知识
    《More Effective C#》读书笔记
    《Effective C#》读书笔记
    《编程匠艺》读书笔记
    《Scrum实战》读书会作业01
    开始一段新的敏捷学习之旅 —— IT帮读书会第4期《Scrum实战》
    【译】别学框架,学架构
    AngularJS学习笔记(1)
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5729582.html
Copyright © 2020-2023  润新知