IT/Programming / / 2023. 4. 27. 09:25

<Effective Java> RULE 30 int상수 대신 enum을 사용하라

반응형

 

일반 int상수 패턴

public static final int APPLE_FUGI = 0;
public static final int APPLE_PIPPIN = 1;    
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
 

단점

· 형 안전성

    int i = (APPLE_FUGI - ORANGE_NAVEL) / APPLE_PIPPIN;
 

· 깨지기 쉬운 프로그램

ㄴ int enum상수는 컴파일 시점 상수이기 때문에 상수를 사용하는 클라이언트 코드와 함께 컴파일된다.

ㄴ 상수의 int값이 변경되면 클라이언트도 다시 컴파일 해야한다.(컴파일이 안되면 결과를 예측할 수 없다.)

 

Enum 자료형

    public enum Apple{PUJI,PIPPIN};
    public enum Orange{NEVEL, TEMPLE};
 

· 각 열거 상수별로 하나의 객체를 public static final필드 형태로 제공

· 클라이언트에서 enum 자료형으로 새로운 객체를 생성하거나 계승할 수 없다.

· 싱글턴 패턴을 일반화 한것(규칙 3)

장점

· 컴파일 시점 형 안전성 제공

ㄴ Apple인자를 받을 때 엉뚱한 자료형 전달하면 컴파일 오류, ==해도 다른 자료형이면 같은 결과

· 같은 이름의 상수가 공존할 수 있다.

ㄴ 이름공간이 분리되어 있다.

· 상수를 추가하거나 순서를 변경해도 클라이언트는 다시 컴파일 할 필요가 없다.

ㄴ 상수를 제공하는 필드가 enum자료형과 클라이언트 사이에서 격리 계층 구실

· toString을 통해 인쇄가능 문자열로 변환 가능

· 임의의 메서드나 필드, 인터페이스를 추가할 수 있다.

 

Enum 자료형 활용법

· 자기 값에 따라 분기하는 Enum자료형

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("Unknown op : " + this);
    }
}
 

swtich문이 다음까지 이어질 수 있기 때문에 throw문 없이는 컴파일 되지 않는다.

새로운 Enum타입을 추가 했을 때 case 또한 추가 해줘야하므로 좋은 코드가 아니다.

// 상수별로 메서드 구현을 이용한 enum자료형
enum Operation2{
    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);
}
 

상수별 메서드를 강제하여 apply의 구현을 잊지 않도록 한다.

 

// 상수별로 클래스 몸체와 데이터를 갖는 enum자료형

enum Operation3{
    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;
    private Operation3(String symbol){this.symbol = symbol;};
    
    @Override public String toString() {return symbol;}

    abstract double apply(double x, double y);
    
    //Enum자료형에 대한 fromString 메서드 구현
    private static final Map<String, Operation3> stringToEnum = new HashMap<String, Operation3>();
    static {
        for (Operation3 op : values()) {
            stringToEnum.put(op.toString(), op);
        }
    }
    
    //문자열이 주어지면 그에대한 Operation 상수 반환, 잘못된 문자열이면 null반환
    public static Operation3 fromString(String symbol) {
        return stringToEnum.get(symbol);
    }
}
 

상수별 데이터와 메서드 구현을 혼용하여 사용, Enum에서 toString을 재정의할 시 fromString메서드를 작성해서 toString이 뱉어내는 문자열을 다시 enum상수로 변환할 수단을 제공

// 정책 enum패턴

enum PayrollDay{
    MONDAY(PayType.WEEKDAY),
    WEDNESDAY(PayType.WEEKDAY),
    FRIDAY(PayType.WEEKEND),
    SUNDAY(PayType.WEEKEND);
    
    private final PayType payType;
    
    private PayrollDay(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 hours, double payRate);
        
        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}
 

결론

언제 Enum을 사용해야 하나?

· 고정된 상수 집합이 필요할 때

· 동일한 메서드가 상수별로 다르게 동작이 필요할 때 Swtich대신 Enum으로

· 여러 Enum 상수가 공통 기능을 이용해야 할때 정책 Enum패턴

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유