Search
Duplicate

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

작성자
챕터
6장 - 열거 타입과 애너테이션
최종 편집
2023/07/29 05:11
생성 시각
2023/07/28 09:26

개요

열거 타입은 확장이 불가능하다. 하지만 특정 경우(ex. 특정 기계가 수행하는 연산을 뜻하는 연산 코드)에는 열거 타입을 확장해야 할 필요가 있다. 열거 타입이 인터페이스를 구현할 수 있다는 사실을 이용해 기존 열거 타입의 한계를 극복해보자

개념 및 용어

타입 토큰
타입 토큰은 클래스 리터럴과 동일한 개념으로 클래스의 타입을 명시하는 토큰이라고 볼 수 있다.
// 선언부 (타입 토큰) void myMethod(Class<?> class) {} // 사용부 (클래스 리터럴) myMethod(String.class);
JavaScript
복사

예제코드

public interface Operation { double apply(double x, double y); }
Java
복사
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; }
Java
복사
열거 타입인 BasicOperation은 확장할 수 없지만, 인터페이스인 Operation은 확장이 가능하다. 이런 방식으로, Operation을 구현한 다른 열거 타입을 정의해 기본 타입인 BasicOperation을 대체할 수 있다.
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; } }
Java
복사
새로 작성한 ExtendedOperation은 Operation 인터페이스를 구현한 연산이 쓰이는 곳은 어디든 쓰일 수 있다.
apply 메서드가 Operation 인터페이스에 선언되어 있기 때문에, 상수별로 메서드를 구현할 때 와는 다르게 열거 타입에 추상 메서드로 선언하지 않아도 된다.
public static void main(String[] args) { double x = Double.parseDouble(args[0]); double y = Double.parseDouble(args[1]); test(ExtendedOperation.class, x, y); }
Java
복사
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)); }
Java
복사
main 메서드는 test 메서드에 한정적 타입 토큰 역할을 하는 ExtendedOperation의 class 리터럴을 넘겨서 (확장된) 연산들이 무엇인지 알려준다.
opEnumType 매개변수의 선언 부분인 <T extends Enum<T> & Operation> 의미 : class 객체가 열거 타입이고 Operation (인터페이스)의 하위 타입(자기 자신을 포함한)이어야 한다.
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); }
JavaScript
복사
class 객체 대신 한정적 와일드카드 타입인 Collection< ? extends Operation>을 인자로 넘긴다.
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)); }
JavaScript
복사
위와 같은 방식을 사용하면 여러 구현 타입의 연산을 조합해 호출할 수 있다.
→ 인터페이스인 Operation의 구현체들의 Collection을 인자로 받기 때문이다.

문제점

열거 타입끼리 구현을 상속할 수 없다.

결론

인터페이스를 사용하면 열거 타입의 확장 불가능성을 우회할 수 있다.

참고자료