Item 38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라

서론

타입 안전 열거 패턴은 확장이 가능하나, 열거 타입은 확장을 할 수 없다
다시 말해 타입 안전 열거 패턴은 값을 그대로 가져온 다음 값을 더 추가하여 다른 목적으로 쓸 수 있지만,
열거 타입은 그럴 수 없다.

하지만 열거타입도 확장할 수 있는 방법이 한 가지 존재한다.
기본적인 아이디어는 열거 타입이 인터페이스를 구현할 수 있다는 사실을 이용하는 것이다.

인터페이스를 이용한 확장 가능 열거 타입

1
2
3
public interface Operation {
double apply(double x, double y);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public enum BasicOperation implements 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;
}
};

private final String symbol;

BasicOperation(String symbol) {
this.symbol = symbol;
}
}

열거 타입인 BasicOperation은 확장할 수 없지만 인터페이스인 Operation은 확장할 수 있고, 이 인터페이스를 연산의 타입으로 사용하면 된다.

다른 열거 타입 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum ExtendedOperation implements Operation {

EXP("^") {
@Override
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
@Override
public double apply(double x, double y) {
return x % y;
}
};

private final String symbol;

ExtendedOperation(String symbol) {
this.symbol = symbol;
}
}

Operation 인터페이스를 구현하면 다른 열거 타입에서도 인터페이스를 구현하여 기능을 확장할 수 있다.

열거 타입 확인

Enum타입을 넘겨 순회하기

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
double x = 4;
double y = 2;
test(ExtendedOperation.class, x, y);
}

public 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));
}
}

  • <T extends Enum<T> & Operation> : 타입이 Enum타입이면서, Operation을 구현하는 클래스

Collection<? extends Operation> 넘겨 순회하기

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
double x = 4;
double y = 2;
test2(Arrays.asList(ExtendedOperation.values()), x, y);
}

public static void test2(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));
}
}

  • 열거 타입의 리스트를 넘겨 <? extends Operation>인 한정적 와일드 카드 타입으로 지정

요약

  • 확장할 수 있는 열거 타입이 필요한 경우 인터페이스를 정의하여 구현하자
  • 열거 타입끼리는 상속이 되지 않는다.
  • 여러 열거 타입 간 공유하는 기능이 있으면, 클래스나 도우미 메서드로 분리하자

참고

  • Effective Java 3rd Edition - Item 38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라