int 상수 대신 열거 타입을 사용하라
public static final int name = 0;
public static final int departmentName = 1;
public static final int companyName = 2;
public static final int phoneNumber = 3;
public static final int email 4;
- 정수 열거 패턴 기법(int enum pattern)에는 단점이 있다.
- 타입 안전을 보장할 수 없다.
- 표현력도 좋지 않다.
- 동등 연산자로 비교하더라도 컴파일러가 아무런 경고 메시지를 출력하지 않는다.
- 컴파일하면 그 값이 클라이언트 파일에 그대로 새겨지기에 상수 값이 바뀌게 되면 클라이언트도 반드시 다시 컴파일해야만 한다.
- 이처럼 정수 열거 패턴(int enum pattern)뿐만 아니라 문자열 열거 패턴(string enum pattern) 기법도 있는데, 이 방식은 더 나쁘다.
- 상수의 의미를 출력할 수 있다는 점에서는 좋으나 하드코딩한 문자열에 오타가 있어도 컴파일러가 확인할 길이 없기에 자연스럽게 런타임 버그가 발생하게 된다. 문자열 비교에 따른 성능 저하 역시 당연한 결과이다.
public static final String blank = "";
public static final String minus = "-";
public static final Stirng atSign = "@";
- 이러한 열거 패턴을 해결해주는 대안이 바로 EnumType이다.
EnumType
- 클래스이다.
- 상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개한다.
- 밖에서 접근할 수 있는 constructor를 제공하지 않기 때문에 인스턴스를 직접 생성하거나 확장 할 수 없기 때문에 EnumType 선언으로 만들어진 인스턴스 들은 딱 하나만 존재함을 보장한다.
- SingleTon은 원소가 하나뿐인 EnumType이라 할 수 있고, 거꾸로 EnumType은 SingleTon을 일반화한 형태라고 볼 수 있다.
- 컴파일타임 안정성을 제공한다.
- 각자의 이름공간이 있어서 이름이 같은 상수도 평화롭게 공존한다.
- EnumType의 toString 메서드는 출력하기에 적합한 문자열을 내어준다.
- 추가로 EnumType은 임의의 메서드나 필드를 추가할 수 있고, 임의의 인터페이스를 구현하게 할 수도 있다.
EnumType 예제 코드
public enum IPhone {
IPONE11(6.1, "A13", 1200),
IPONE11PRO(5.8, "A13", 1200),
IPONE11PROMAX(6.5, "A13", 1200),
IPONESE(4.7, "A13", 700),
IPONE12(6.1, "A14", 1200),
IPONE12MINI(5.4, "A14", 1200),
IPONE12PRO(6.1, "A14", 1200),
IPONE12PROMAX(6.7, "A14", 1200);
private final double inch;
private final String process;
private final int cameraPixel;
prvate static final double centimeter = 2.54;
IPone(double inch, String process, int cameraPixel) {
this.inch = inch;
this.process = process;
this.cameraPixel = cameraPixel;
}
public double inch(); // private으로 변경 필요
public String process(); // private으로 변경 필요
public int cameraPixel(); // private으로 변경 필요
public double calcurateInchToCentimeter(doublic inch) {
return inch * centimeter;
}
}
- EnumType을 각각 특정 데이터와 연결지으려면 생성자에서 데이터를 받아 초기화해 인스턴스 필드에 저장하면 된다.
- 이 때, EnumType은 근본적으로 불변이라 모든 필드는 final이어야 한다.
- 필드를 public으로 선언해도 되지만, private으로 두고 별도의 public 접근자 메서드를 두는게 낫다.
values
- EnumType은 자신 안에 정의된 상수들의 값을 배열에 담아 반환하는 정적 메서드인 values를 제공한다.
상수별 메서드 구현(constant-specific method implementaltion)
public enum Operation {
PLUS("+") {
public douvle apply(double x, double y) { return x + y };
},
MINUS("-") {
public douvle apply(double x, double y) { return x - y };
},
TIMES("*") {
public douvle apply(double x, double y) { return x * y };
},
DIVIDE("/") {
public douvle 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);
}
- EnumType은 상수별로 다르게 동작하는 코드(=추상 메서드)를 구현하는 더 나은 수단을 제공한다.
전략 열거 타입 패턴
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 {
int overtimePay(int minutesWorked, int payRate) {
return minutesWorked <= MINS_PER_SHIFT ? 0 : (minutesWorked - MINS_PER_SHIFT) * payRate / 2;
}
},
WEEKEND {
int overtimePay(int minutesWorked, int payRate) {
return minutesWorked * payRate / 2
}
};
abstract int overtimePay(int minutesWorked, int payRate);
private static final int MINS_PER_SHIFT = 8 * 60;
int pay(int minutesWorked, int payRate) {
int basePay = minutesWorked & payRate;
return basePay + overtimePay(minutesWorked, payRate);
}
}
}
- 열거 타입 상수 일부가 같은 동작을 공유한다면 매번 추가하기보다 해당 패턴을 사용하는게 수정을 최소화하자.