🏠 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
기본적으로 열거 타입은 기존 상수를 그대로 가져와, 새롭게 추가하여 다른 목적으로 사용할 수 없다.
그런데 확장할 수 있는 열거 타입의 쓰임이 어울리는 경우가 하나는 있는데, 그건 바로 연산 코드이다.
연산 코드의 각 원소는 특정 기계가 수행하는 연산을 뜻한다. ( 예를 들어 계산기 )
가끔 API가 제공하는 기본 연산 외에 사용자마다 확장 연산을 추가해줘야 할 때가 있다.
이때 사용할 수 있는게 바로 열거 타입이다.
열거 타입은 임의의 인터페이스를 구현할 수 있다.
연산 코드용 인터페이스를 정의하고, 열거 타입이 해당 인터페이스를 구현하면 된다.
그러면 열거 타입이 인터페이스의 표준 구현체 역할을 한다.
public interface Operation {
double apply(double x, double y);
}
public enum BassicOperation 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; }
},
DEVIDE("/") {
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; }
}
열거 타입인 BasicOperation 은 확장시킬 수 없지만, Operation 인터페이스를 확장할 수 있다.
요구사항에 맞게 Operation 을 또 다른 열거 타입으로 확장해 BasicOperation 을 대체할 수도 있다.
우리는 BasicOperation 이 아닌 Operation 인터페이스를 사용하도록 작성하기 때문에, 기존 연산을 쓰던 곳이면 어디서든 새롭게 추가한 Operation 구현체를 사용할 수 있다.
또한 apply 메서드가 인터페이스에 선언되어 있기 때문에 열거 타입에 추상메서드로 작성하지 않아도 된다.
사용법은 총 두가지가 존재한다.
1. 기본 열거 타입 대신 확정된 열거 타입의 리터럴 매개변수 전달
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
test(BasicOperation.class,x,y);
}
private static <T extends Enum<T> & Operation> //Class 객체가 열거 타입인 동시에 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));
}
main 메서드는 test 메서드에 BasicOperation 의 class 리터럴을 넘겨 확장된 연산들이 무엇인지 알려준다.
2. Class 객체 대신 한정적 와일드카드 타입인 Collection<? extends 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);
}
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));
}
덜 복잡하고 test 메서드에 유연성이 조금 더 생겼다. 즉 여러 구현 타입의 연산을 조합해서 호출할 수 있게 되었다.
그러나 EnumSet, EnumMap 을 특정 연산에서는 사용하지 못한다.
인터페이스를 확장해 열거 타입을 구현하는 방식에도 한가지 문제점이 존재한다.
열거 타입끼리는 구현을 상속할 수 없는 것이다. 때문에 모든 Operation 구현에 들어갈 기호를 저장하고 찾는 로직은 구현체에 중복으로 들어갈 수 밖에 없다.
공유하는 기능이 많다면 해당 부분을 별도의 도우미 클래스 혹은 정적 도우미 메서드로 분리하는 방식을 통해 코드 중복을 없앨 수 있다.
'개인 공부 > 스터디' 카테고리의 다른 글
[Real MySQL] 인덱스 (0) | 2023.08.03 |
---|---|
[이펙티브 자바] 열거(enum) 타입 (0) | 2023.07.04 |
[이펙티브 자바] 제네릭 메서드 작성법 (0) | 2023.06.26 |
[이펙티브 자바] 제네릭 (0) | 2023.06.21 |
[이펙티브 자바] 인터페이스의 용도 (0) | 2023.06.20 |