38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
열거(enum)타입이란?
고정된 상수들의 집합을 표현하는 특별한 종류의 클래스
열거타입의 특징
고정된 상수 집합을 표현할 때 사용한다
다른 클래스를 상속할 수 없다
일반적으로 열거 타입을 확장하는 것은 좋지 않다
열거타입을 확장하려면? 인터페이스를 이용하자!
예시
공통 기능을 담은 인터페이스
public interface Operation {
double apply(double x, double y);
}
기본 연산 기능을 담은 열거 타입
public enum BasicOperation implements 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;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
확장 연산 기능을 담은 열거 타입
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
사용 방법
📌 방법 1 : 클래스 타입 전달 (한정적 타입 매개변수 사용)
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
test(ExtendedOperation.class, x, y);
}
private static <T extends Enum<T> & Operation> void test(
Class<T> opEnumType, double x, double y) {
for (Operation op : opEnumType.getEnumConstants())
System.out.printf("%f %s %f = %f%n",
x, op, y, op.apply(x, y));
}
✔️ 특징
클래스 타입 (Class) 을 전달해야 한다
전달받은 클래스에서 enum 상수 전체를 자동으로 얻을 수 있다
타입 안전하고, enum을 기반으로 반복할 때 유리하다
✅ 장점
enum 클래스 하나를 통째로 테스트할 때 편리하다
Enum.getEnumConstants()를 통해 열거 상수를 자동으로 얻을 수 있다
❌ 단점
반드시 한 enum 클래스 전체를 테스트해야 한다
EarthOperation과 MarsOperation을 같이 넣을 수 없다
📌 방법 2 : 컬렉션 전달 (한정적 와일드카드 사용)
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
test(Arrays.asList(ExtendedOperation.values()), x, y);
}
private static void test(Collection<? extends Operation> opSet,
double x, double y) {
for (Operation op : opSet)
System.out.printf("%f %s %f = %f%n",
x, op, y, op.apply(x, y));
}
✔️ 특징
Set이나 List 같은 컬렉션을 직접 전달한다
Operation 인터페이스를 구현한 상수라면 무엇이든 섞어서 전달 가능하다
✅ 장점
서로 다른 enum 타입의 상수를 섞어서 테스트할 수 있다 (예: BasicOperation + ExtendedOperation)
사용할 연산만 선택적으로 골라서 넘길 수 있다
❌ 단점
enum 클래스처럼 자동으로 상수를 가져올 수는 없다 ➔ 직접 Set.of(...) 같은 방식으로 수동으로 넘겨야 한다
다른 enum 의 상수에 대해선 EnumSet, EnumMap 이 제한된다.
Set<MarsOperation> s = EnumSet.of(MarsOperation.MARS_PLUS, MarsOperation.MARS_MINUS); // 가능 Set<Operation> s = EnumSet.of(EarthOperation.MINUS, EarthOperation.PLUS); // 불가능 Set<Operation> s = EnumSet.of(EarthOperation.MINUS, MarsOperation.MARS_PLUS); // 불가능
자바 라이브러리의 예시
public enum LinkOption implements OpenOption, CopyOption {
/**
* Do not follow symbolic links.
*
* @see Files#getFileAttributeView(Path,Class,LinkOption[])
* @see Files#copy
* @see SecureDirectoryStream#newByteChannel
*/
NOFOLLOW_LINKS;
}
❗️ 주의!
열거 타입끼리는 상속이 불가능하다
구현 코드가 중복될 수 있다 (각 열거 타입이 비슷한 코드를 가질 수 있음)
코드 중복이 많아지면 도우미 클래스나 정적 메서드로 분리하는 것이 좋다
🧩 어려웠던 점
처음에는 열거 타입의 제약(상속 불가)이 왜 있는지 이해하기 어려웠다
인터페이스를 통한 확장이라는 개념이 처음에는 복잡해 보였다
💭 느낀 점
같은 연산 인터페이스를 구현하면, 여러 enum을 조합해서 쓸 수 있다는 점이 아주 유용하다고 느꼈다
가끔은 제약이 있는 것이 더 안전한 코드를 작성하는 데 도움이 된다는 것을 느꼈다
Last updated