// 34-1. 정수 열거 패턴
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
// 문자열 열거 패턴
public static final String APPLE_FUJI = "APPLE_FUJI";
public static final String APPLE_PIPPIN = "APPLE_PIPPIN";
public static final String APPLE_GRANNY_SMITH = "APPLE_GRANNY_SMITH";
public static final String ORANGE_NAVEL = "ORANGE_NAVEL";
public static final String ORANGE_TEMPLE = "ORANGE_TEMPLE";
public static final String ORANGE_BLOOD = "ORANGE_BLOOD";
정수 열거 패턴보다 더 안좋다.
하드코딩하게 만든다.
오타가 있어도 컴파일에서 확인이 불가능하니 런타임에 오류가 생긴다.
새로운 방식
// 34-2. 열거 타입
public enum Apple {
FUJI, PIPPIN, GRANNY_SMITH
}
public enum Orange {
NAVEL, TEMPLE, BLOOD
}
일종의 클래스이다.
컴파일타임 타입 안전성을 제공한다.
상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개한다.
안에 구조로 보면 이렇게 public static final 로 되어있다.
클라이언트가 인스턴스를 직접 생성하거나 확장할 수 없으니 인스턴드들은 딱 하나씩만 존재한다.
다시말해 인스턴스 통제, 싱글턴이다.
거꾸로 열거 타입은 싱글턴을 일반화한 형태라고 볼 수 있다.
열거 타입에는 toString(), Object 메서드들을 높은 품질로 구현해 놨다.
Comparable, Serializable을 구현해놨다.
열거타입의 메서드와 필드
열거 타입에는 어떤 메서드도 추가할 수 있다.
단순하게는 상수 모음일 뿐이지만, 실제로는 클래스이므로 고차원의 추상 개념 하나를 완벽히 표현해낼 수도 있다.
// 34-3. 여덟개의 행성을 나타낸 열거 타입
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;
this.surfaceGravity = G * mass / (radius * radius);
}
// getter ...
public double surfaceWeight(double mass) {
return mass * surfaceGravity;
}
}
열거 타입 상수 각각을 특정 데이터와 연결지으려면 생성자에서 데이터를 받아 인스턴스 필드에 저장하면된다.
열거 타입은 근본적으로 불변이라 모든 필드는 final 이어야 한다.
필드는 private 으로 하고 접근자 메서드를 둔다.
열거 타입에서는 values() 를 제공하는데 자신안에 정의된 상수들의 값을 배열에 담아 반환한다.
열거 타입을 선언한 클래스 혹은 그 패키지에서만 유용한 기능은 private이나 package-privat 메서드로 구현한다.
상수별 메서드 구현
public enum 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 abstract double apply(double x, double y);
}
apply() 가 상수 선언 바로 옆에 있으니 새로운 상수를 추가할 때도 apply()를 재정의 해야한다는 사실을 깜빡하기 힘들다.
또한 apply() 가 추상메서드이므로 재정의하지 않았다면 컴파일 오류로 알려준다.
전략 열거 타입
// 급여명세서
public enum PayrollDay {
MONDAY(WEEKDAY),
TUESDAY(WEEKDAY),
WEDNESDAY(WEEKDAY),
THURSDAY(WEEKDAY),
FRIDAY(WEEKDAY),
SATURDAY(WEEKEND),
SUNDAY(WEEKEND),
;
private final PayType payType;
PayrollDay(PayType payType) {
this.payType = payType;
}
int pay(int minutesWorked, int payRate) {
return payType.pay(minutesWorked, payRate);
}
// 전략 열거 타입
enum PayType {
WEEKDAY {
@Override
int overtimePay(int minsWorked, int payRate) {
return minsWorked <= MINS_PER_SHIFT ? 0 :
(minsWorked - MINS_PER_SHIFT) * payRate / 2;
}
},
WEEKEND {
@Override
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;
int pay(int minsWorked, int payRate) {
int basePay = minsWorked * payRate;
return basePay + overtimePay(minsWorked, payRate);
}
}
}
기존의 switch 문은 간결하지만 관리 관점에서는 위험한 코드이다, 왜냐면 새로운 값을 열거 타입에 추가하면 case 문에 잊지 말고 넣어줘야 한다.