effective java 第6章 枚举和注解 第30条 用enum代替int常量

枚举是什么?

       枚举类型是指由一组固定的常量组成合法值的类型。枚举类型是解决常量int的另一种解决方案。

枚举类型的简单示例:

public enum AlarmPoints {STAIR1,STAIR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN}

枚举的优势:

     枚举类型是真正的final.所以客户端即不能创建,也不能对它进行扩展,因此很可能没有实例,而只有申明过的枚举常量。换句话说,枚举类型是实例受控的。它们是单例的泛型化。

     枚举类型提供检查异常。

     命名直观,可读性强。

     实现了Comparable和Serialicable 接口。

 

简单枚举类型对于大多数枚举类型来说,已经够用了,但有的时候你可能需要更多的方法。有的时候,你需要将本质不同的行为和每个常量联系起来。比如,编写4中计算方式的枚举类型:

public enum Operation {
    PLUS,
    MIMUS,
    TIMES,
    DIVIDE;
    double apply(double x,double y){

        switch(this){
            case PLUS: return x + y;
            case MIMUS: return x - y;
            case TIMES: return x * y;
            case DIVIDE:return x / y;
        }
        throw new AssertionError("Unknown op:" + this);
    }
}

这段代码看似可行,只是不太好看,如果没有throw语句,就没有办法编译通过,但是实际上不可能知道最后的语句。最糟糕的是,如果你添加了新的枚举类型,而switch中却没有添加,编译异常通过,运行却会出错。有一种解决方案:

public enum Operation {
    PLUS{
        @Override
        double apply(double x, double y) {
            return  x + y;
        }
    },
    MIMUS {
        @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);
}

在这中写法下,你添加新常量,编译器会提醒你重写抽象方法,你也必须重写抽象方法;

它可以和特定的符号结合起来:

public enum Operation {
    PLUS("+"){
        @Override
        double apply(double x, double y) {
            return  x + y;
        }
    },
    MIMUS ("-"){
        @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;
        }
    };
    private final String sysmbol;

    Operation(String sysmbol){
        this.sysmbol = sysmbol;
    }

    @Override
    public String toString() {
        return sysmbol;
    }

    abstract double apply(double x, double y);
}
    public static void main(String[] args) {
        double x = 2.0;
        double y = 4.0;
        Arrays.asList(values()).forEach(o->{
            System.out.printf("%f %s %f = %f%n",x,o,y,o.apply(x,y));
        });
    }

2.000000 + 4.000000 = 6.000000
2.000000 - 4.000000 = -2.000000
2.000000 * 4.000000 = 8.000000
2.000000 / 4.000000 = 0.500000

打印出了很工整的表单。

枚举类型有一个valueOf(String name)方法,可以将字符串转换成真是枚举类型,由于覆盖了toString 方法,所以编写一个fromString方法;

    private static final Map<String,Operation> stringToEnum = new HashMap<>();
    static {
        Arrays.asList(Operation.values()).forEach(o-> stringToEnum.put(o.toString(),o));
    }

    public static Operation fromString(String symbol){
        return stringToEnum.get(symbol);
    }

特定于常量的方法实现中有一个美中不足的地方,它们使得枚举常量中共享代码变得更加困难了。比如,考虑用一个枚举表示薪资包中的工作天数。这个枚举中有一个方法,根据给定某工人的基本工资以及当天的工作时间,来计算它当天的报酬。在五个工作日中,超过8个小时算加班,加班有额外工资,周六,周天,全天算加班。

public enum PayrollDay {
    MON,TUE,WED,THU,FRI,SAT,SUN;
    private static final int HOUTS_PER_SHIFT = 8;

    public double get(final int hours,double payRate){
        double base = hours * payRate;
        double over;
        switch (this){
            case SAT:case SUN:
                over = hours * payRate * 0.5;
                default: over = hours >= HOUTS_PER_SHIFT ?
                        (hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
        }
        return base + over;
    }
}

代码非常简洁,但是确难以维护,如果某人有一个工作日,确实放假,但是又忘记添加switch,程序依然可以运行,只是结果不正确而已。

 

你真正想要的就是每当添加一个枚举常量是,就强制选择一种加班报酬策略,又一种很好的方法可以实现这一点,添加一个枚举策略,来实现对加班工资的计算,虽然这种方式没有那么简洁,但却更加安全,也更加灵活:

public enum PayrollDay {
    MON(PayType.WEEKDAY),TUE(PayType.WEEKDAY),WED(PayType.WEEKDAY),
    THU(PayType.WEEKDAY),FRI(PayType.WEEKDAY),
    SAT(PayType.WEEKEND),SUN(PayType.WEEKEND);
    //支付方式
    private final PayType payType;
    //初始化payType
    PayrollDay(PayType payType){
        this.payType = payType;
    }
    //计算
    public double pay(final int hours,double payRate){
        return payType.get(hours,payRate);
    }


    enum PayType{
        WEEKDAY {
            @Override
            double overGet(int hours, double payRate) {
                return hours >= HOUTS_PER_SHIFT ?
                        (hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
            }
        },
       WEEKEND {
           @Override
           double overGet(int hours, double payRate) {
               return  hours * payRate * 0.5;
           }
       };
       private static final int HOUTS_PER_SHIFT = 8;

       abstract double overGet(final int hours,double payRate);
        public double get(final int hours,double payRate){
            double base = hours * payRate;
            double over = overGet(hours,payRate);
            return base + over;
        }
   }
}

测试:

    public static void main(String[] args) {
        double s = SUN.pay(10,10);
        double s1 = MON.pay(10,10);
        double s2 = MON.pay(7,10);
        System.out.println(s + "..." + s1 + "..." + s2);
    }

150.0...110.0...70.0

枚举中的switch语句适合于给外部的枚举类型增加特定于常量的行为,或改变,或替换

public class Demo{
    public static Operation inverse(Operation o){
        switch (o){
            case PLUS: return MIMUS;
            case MIMUS:return PLUS;
            case TIMES:return DIVIDE;
            case DIVIDE:return TIMES;
            default: throw new AssertionError("Unknow o:" + o);
        }
    }
}

这里使用了静态导入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值