정수 열거 패턴의 문제점
name, color 등 자주 사용되는 기본 요소들을 구분하기 위해 접두어를 붙여야 한다 ex) APPLE_COLOR, BANANA_COLOR...
상수의 값이 바뀌면 클라이언트도 다시 컴파일해야한다. 컴파일하지 않을 시 이전에 사용하던 상수 값을 사용하여 의도치 않게 동작할 것이다.
문자열로 출력하기 까다롭다. 디버거로 살펴보아도 단순 숫자로 표현되기에 디버깅에 좋지 않다.
그룹에 속한 모든 상수를 순회할 수도 없으며, 몇 개가 있는지도 알 수 없다.
문자열 열거 패턴의 문제점
상수가 아닌 의미가 있는 값을 출력할 순 있으나 문자열 상수의 이름 대신 문자열 값을 그대로 사용하는 실수를 하기 쉽다.
문자열 비교는 성능에 좋지 않다.
열거 타입
클래스이다.
내부에서 사용하는 상수 하나 당 public static final 필드로 공개한다.
싱글턴을 일반화한 형태라고 볼 수 있으며 각 인스턴스가 딱 하나만 존재하는 것이 보장된다.
해당 열거 타입의 값 또는 null 값과만 비교될 수 있으므로 타입 안전성을 제공하며, 컴파일 시점에 에러를 확인할 수 있다.
열거 타입은 각자의 이름 공간을 가져, 다른 열거 타입이라면 같은 이름의 상수도 사용할 수 있다.
새로운 상수를 추가하거나 순서를 바꾸어도 클라이언트는 필드의 이름밖에 모르기 때문에 다시 컴파일할 필요가 없다.
삭제된 상수를 직접 참조하고 있는 클라이언트 역시 컴파일 오류를 통해 유의미한 에러를 확인할 수 있다.
toString 메서드를 통해 문자열로 출력하기 간편하다.
Object, Comparable, Serializable의 메서드들을 높은 품질로 구현하여 클래스처럼 사용 가능하다.
임의의 메서드나 필드를 추가할 수 있으며, 인터페이스를 구현하게 할 수도 있다.
열거 타입 사용
필드를 추가하고, 생성자를 두어 초기화하여 클래스처럼 다룰 수 있다. ex) Fruit.APPLE.getColor()
values 메서드를 통해 상수들을 순회할 수 있다.
valueOf(String) 메서드를 통해 이름에 해당하는 String 을 enum 값으로 바꿀 수 있다.
열거 타입의 생성자에서는 정적 필드 중 상수 변수만 접근할 수 있다.
사칙연산 프로그램을 위해 Operation 열거 타입을 PLUS, MINUS, TIMES, DEVIDE 를 갖도록 정의했다고 하자.
apply(double x, double y) 라는 메서드를 구현할 때, 일반적인 방법으로는
public double apply(double x, double y) {
switch(this) {
case PLUS: return x + y;
case MINUS: return x - y;
...
}
throw new AssertionError("");
}
위와 같이 구현할 수 있다. 그러나 도달할 일 없는 에러를 던져야하며(기술적으로 도달할 수는 있기에 컴파일 에러가 난다) 상수가 추가되면 메서드마다 case 를 추가해주어여한다.
상수별 메서드 사용
열거 타입에는 추상 메서드를 선언하고, 각 상수별로 메서드를 재정의하는 방법
public enum Operation {
PLUS {public double apply(double x, double y){return x + y;}}
MINUS {public double apply(double x, double y){return x - y;}}
...
public abstract double apply(double x, double y);
}
위 문제점들을 모두 해결할 수 있다.
그러나, 메서드에서 수행하는 일이 많아지거나, 특정 상수만 다른 로직을 사용한다면 코드 중복이 발생하게 된다.
도우미 메서드를 추출하여 상수별로 가져다 사용할수도 있지만, 도우미 메서드 내에서 다시 switch 문을 사용하게 된다.
전략 열거 타입 패턴
private 중첩 열거 타입으로 상수별로 필요한 메서드들을 미리 구현해놓고, 어느 것을 사용할 지 선택하도록 한다.
도우미 메서드 내에서 다시 switch 문을 사용하므로 그 switch 문을 상수별 메서드로 한번 더 추출한 것으로 볼 수 있을 것 같다.
추가.
열거 타입의 성능은 정수 상수와 별반 다르지 않다.
중요.
필요한 원소를 컴파일타임에 다 알 수 있는 상수 집합이라면 항상 열거 타입을 사용하자.
열거 타입에 정의된 상수 개수가 영원히 고정, 불변일 필요는 없다.