Effective Java 学习笔记之枚举

本文介绍了如何使用枚举类型替代int常量、实例域及序数,并通过具体示例展示了枚举类型的强大功能,包括利用枚举实现策略模式、枚举实例的自定义行为以及通过接口实现可扩展的枚举。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.900000
1.300000 - 2.600000 = -1.300000
1.300000 * 2.600000 = 3.380000
1.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.300000
1.300000 EXP 2.600000 = 1.978120
1.300000 MOD 2.600000 = 1.300000
1.300000 EXP 2.600000 = 1.978120

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值