NMP-Study / EffectiveJava2022

Effective Java Study 2022
5 stars 0 forks source link

아이템 34. int 상수 대신 열거 타입을 사용하라 #34

Closed okhee closed 2 years ago

RulLu16 commented 2 years ago

열거(enum) 타입을 제공하기 이전의 상수 선언

public static final int ORANGE_NAVEL = 0; // 귤? public static final int ORANGE_TEMPLE = 1; // 귤? public static final int ORANGE_BLOOD = 2; // 붉은색 오렌지

- **타입 안전을 보장할 방법이 없다.**
  - 예를 들어서 오렌지를 건네야 하는 메서드에 사과를 보내고 동등 연산자(==)로 비교해도 아무런 경고가 없다.
- 표현도 애매하다.
  - 사과용 상수의 이름은 `APPLE_`, 오렌지용 상수는 `ORNAGE_` 라는 prefix를 사용해 이름 충돌을 방지한다.
- 문자열로 출력하기도 다소 까다롭다.
  - 출력하거나 디버거로 살펴봐도 결국 숫자로만 보이기 때문..
- 정수 열거그룹을 순회하기도 힘들다.

## 열거 타입(enum type)
```java
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }

열거 타입의 장점?

열거 타입의 예시

데이터와 메서드를 갖는 열거 타입

상수가 더 다양한 기능을 제공했으면 한다?

상수별 메서드 구현(constant-specific method implementation)

- apply 메서드가 추상 메서드이므로 재정의하지 않았다면 컴파일 오류로 알려준다.
- 상수별 메서드 구현을 상수별 데이터와 결합할 수도 있다.
  - 예를 들어 아래와 같이 Operation의 toString을 재정의하여 해당 연산을 뜻하는 기호를 반환할 수 있다!
```java
public class EffectiveJava34 {
    public static void main(String []args){
        double x = Double.parseDouble("2");
        double y = Double.parseDouble("3");
        for (Operation op : Operation.values()) {
            System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
        }

    }
}

enum Operation {
    PLUS("+") { 
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        public 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;
    }

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

// 출력
2.000000 + 3.000000 = 5.000000
2.000000 - 3.000000 = -1.000000
2.000000 * 3.000000 = 6.000000
2.000000 / 3.000000 = 6.000000

// get 하는 방식 public static Optional fromString(String symbol) { return Optional.ofNullable(stringToEnum.get(symbol)); }


## 그런데, 이런 상수별 메서드에도 단점은 있다.
- 열거 타입 상수끼리 코드를 공유하기 어렵다. 예를 들어보면?
```java
enum PayrollDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
    SATURDAY, SUNDAY;

    private static final int MINS_PER_SHIFT = 8 * 60; // 하루 8시간

    int pay(int minutesWorked, int payRate) {
        int basePay = minutesWorked * payRate;

        int overtimePay;
        switch(this) {
            case SATURDAY: case SUNDAY: // 주말
                overtimePay = basePay / 2;
                break;
            default: // 주중
                if (minutesWorked <= MINS_PER_SHIFT) {
                    overtimePay = 0 ;
                } else {
                    overtimePay = (minutesWorked - MINS_PER_SHIFT) * payRate / 2;
                }
        }
        return basePay + overtimePay;
    }
}

전략 열거 타입 패턴

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

    private final PayType payType;

    PayrollDay() {
        this(PayType.WEEKDAY);
    }

    PayrollDay(PayType payType) {
        this.payType = payType;
    }

    enum PayType {
        WEEKDAY {
            int overtimePay(int minsWorked, int payRate) {
                int overtimePay;
                if (minsWorked <= MINS_PER_SHIFT) {
                    overtimePay = 0;
                } else {
                    overtimePay = (minsWorked - MINS_PER_SHIFT) * payRate / 2;
                }
                return overtimePay;
            }
        },
        WEEKEND {
            int overtimePay(int minsWorked, int payRate) {
                return minsWorked * payRate / 2;
            }
        };

        abstract int overtimePay(int mins, int payRate);

        private static final int MINS_PER_SHIFT = 8 * 60; // 하루 8시간

        int pay(int minutesWorked, int payRate) {
            int basePay = minutesWorked * payRate;
            return basePay + overtimePay(minutesWorked, payRate);
        }
    }
}

정리하면!