Item 30. int 상수 대신 enum을 사용하라.
public enum Apple{ FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BOOD }

장점

아래의 코드는 깨지기 쉬운 코드 이다. enum자료형이 늘어 날때 마다 switch문도 같이 고쳐야 한다.

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

더 좋은 방법은 enum자료형에 abstract double apply(double x, double y); 선언하고, 각 상수별 클래스 몸체(constant-specific class body)안에서 실제 함수를 재정하는 것이다.

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

    abstract double apply(double x, double y);
}

상수별로 함수 구현은 상수별 데이터와 혼동될수 있기 때문에 아래와 같이 toString을 재정하여 기호를 반한하게 할수도 있다.

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

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

    abstract double apply(double x, double y);
}

public static void main(String[] args){
    double x = 10; // Double.parseDouble(args[0]);
    double y = 20; // Double.parseDouble(args[1]);
    Arrays.stream(Operation.values())
            .forEach(op->
                    System.out.printf("%f %s %f = %f%n",x, op, y, op.apply(x,y)));
}

중복되는 행위(정책)이 필요한 경우, 아래처럼 하위 정책 enum에 위임해서(전략 enum(strategy enum)) 다형성과 재사용성을 확보할 수 있다.

enum PayrollDay {
    MONDAY(PayType.WEEKDAY),    TUESDAY(PayType.WEEKDAY),
    WEDNESDAY(PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY),
    FRIDAY(PayType.WEEKDAY),    SATURDAY(PayType.WEEKDAY),
    SUNDAY(PayType.WEEKEND);

    private final PayType payType;
    PayroleDay(PayType payType) { this.payType = payType; }

    double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }
    // 정책 enum 자료형
    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);
        }
    }
}

결론