본문 바로가기
Java/Effective Java 3E

[이펙티브자바 3판] ITEM38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라

by 잭피 2020. 11. 18.

이번장의 핵심은...

열거 타입 자체는 확장할 수 없지만, 인터페이스와 그 인터페이스를 구현하는 기본 열거 타입을 함께 사용해 같은 효과를 낼 수 있다


열거 타입은 확장할 수 없다

타입 안전 열거 패턴은 열거한 값들에 값을 더 추가하여 다른 목적으로 사용할 수 있지만, 열거 타입은 불가능

확장할 수 있는 방법?

열거 타입이 인터페이스를 구현할 수 있다는 아이디어를 이용

연산 코드용 인터페이스를 정의하고 열거 타입이 이를 구현하도록 함

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

열거타입인 BasicOperation은 확장할 수 없지만, 인터페이스 Operation은 확장이 가능

아래처럼 Operation을 구현해 또 다른 열거 타입을 정의할 수 있음

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

apply 메소드는 인터페이스에 선언되어 있으니 열거 타입에 따로 추상 메서드로 선언하지 않아도 됨

개별 인스턴스 수준에서뿐 아닌 타입 수준으로도 사용가능 2가지 방식

public static void main(String[] args) {
    1. test(ExtendedOperation.class, x. y)
    2. test(Arrays.asList(ExtendedOperation.values()), 2.0, 4.0);
}

// 1. class 리터럴은 열거타입이면서 Operation 하위 타입 (opEnumType)
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));
    }
// 2. Class 대신 한정적 와일드카드 타입을 넘김
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));
    }

인터페이스를 이용해 확장 가능한 열거 타입을 만드는 방식에도 한 가지 문제가 있음

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

따라서 같은 인터페이스를 구현한 열거 타입에 각각에 필요한 메서드를 구현해야함

공유하는 기능이 많다면 별도의 도우미 클래스나 정적 도우미 메소드로 분리하는 방식으로 코드 중복을 피하자

 

 

이 글은 “이펙티브 자바 3판” 책 내용을 정리한 글입니다.

만약 저작권 관련 문제가 있다면 “shk3029@kakao.com”로 메일을 보내주시면, 바로 삭제하도록 하겠습니다.

 

댓글