[Effective Java 3/E] ITEM 38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라.

    Effective Java 표지. Source : https://blog.insightbook.co.kr/

    *알림 : *
    Effective Java 3판은 Java 9까지 도입된 언어적 기능을 중심으로 서술되어 있습니다. 10버젼 이후의 Java 개발을 하시는 분들은 우회적인 접근법 대신 Java 언어 내 새로 도입된 기능이 더 간결하고 좋을 수 있습니다.

    해당 포스팅은 SSAFY 내 책읽기 스터디의 활동을 통해 작성된 포스팅입니다.
    https://github.com/kjsu0209/JavaBook
    https://medium.com/javabook

     

    kjsu0209/JavaBook

    책읽기 스터디. Contribute to kjsu0209/JavaBook development by creating an account on GitHub.

    github.com

     

    JavaBook – Medium

    Documentation space of our book study.

    medium.com

     

     

    Enum type은 확장을 할 수 없다. OOP의 상속의 시점으로 본다면, 확장한 타입들의 원소는 상위 타입의 원소가 되지만, 그 반대는 성립하지 않는다. 순회할 방법도 마땅치 않다. 설계와 구현의 난이도도 올라갈 것이다.

    그러나 Enum 타입은 인터페이스를 구현할 수 있다.

    예제 1 : 연산 코드(Operation Code)

    // 인터페이스 정의
    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;
        }
    }
    

    Enum 타입인 BasicOperation*은 확장할 수 없지만, *Operation 인터페이스는 확장이 가능하다. 사칙연산 뿐 아니라 mod 연산과 exp 지수연산을 추가하고 싶다고 가정하자. Operation 인터페이스를 확장해 구현하기만 하면 된다.

    예제 1-1 : 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;
        }
    }

    인터페이스를 확장한 덕에, 처음 고안했던 BasicOperation 뿐 아니라 ExtendedOperation도 인터페이스를 통해 접근할 수 있다.

    예제 2 : 확장된 Enum 타입을 인터페이스로 접근하기

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

    ExtendedOperation.values()*는 상수의 배열을 반환한다. 앞서 다뤘던 아이템에서 보았듯이 가능하면 배열보다는 List를 사용하길 권장하므로, *Arrays.asList() 메서드로 배열을 리스트로 변환한다.

    한정적 타입을 통해 Operation을 확장한 무언가를 담고있는 Collection을 test 메서드에서 인자로 받는다.

    확장한 타입의 코드 중복작성을 줄이는 테크닉

    그러나 열거타입끼리 상속을 구현할 수 없는 문제는 여전하다. 그러나 확장한 Enum 타입끼리 많은 로직을 공유해야 한다면?

    1. 별도의 Helper class 작성
    2. Static helper method로 분리
    3. 인터페이스의 default 메서드

    댓글