ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
    책/이펙티브자바 2021. 8. 29. 12:02
    • 열거 타입은 거의 모든 상황에서 타입 안전 열거 패턴보다 우수하다.

    타입 안전 열거 타입

    // 이전에 쓰던 방식 
    public final class Season {
      public static final Season SPRING = new Direction("SPRING");
      public static final Season SUMMER = new Direction("SUMMER");
      public static final Season FALL = new Direction("FALL");
      public static final Season WINTER = new Direction("WINTER");
    
        // ...
    }

    열거 타입의 단점

    • 타입 안전 열거 패턴은 확장이 가능하지만 열거 타입은 확장할 수 없다.
    • enum 은 java.lang.Enum 을 확장하고 있음.

    대부분의 상황에서 열거 타입을 확장하는건 좋지 않은 생각이다.

    • 확장한 타입의 원소는 기반 타입의 원소로 취급하지만 그 반대는 성립하지 않는다면 이상하다.
    • 상위, 하위타입의 모든 원소를 순회하는 방법이 없다.
    • 확장성을 높이려면 고려해야 할 요소가 늘어나 설계와 구현이 더 복잡해진다.

    가끔은 enum 을 확장해야 할 수도 있다.

    • 연산코드, 기계마다 가지는 기계가 할 수 있는 연산.
    • 인터페이스 구현을 통해 해결한다.
    • 연산 코드용 인터페이스를 정의하고 열거타입이 구현하도록 한다. 이 열거타입이 표준 구현체 역할을 한다.
    // Operation
    public interface Operation {
      double apply(double x, double y);
    }
    
    // BasicOperation
    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;
      }
    }
    
    // ExtendedOperation
    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;
     }
    }

    타입 수준으로 다형성을 적용할 수 있다.

    // 1. 한정적 타입 매개변수
    public static void main(String[] args) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        test(ExtendedOperation.class, x, y);
    }
    
    private static <T extends Enum<T> & Operation> void test(
        Class<T> opEnumType, double x, double y) {
        for (Opertaion op : opEnumType.getEnumConstants())
            System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
    }
    • (<T extends Enum & Operation> Class): Class 객체가 열거 타입인 동시에 Operation의 하위 타입이어야 한다는 뜻이다.
    // 2. 한정적 와일드 카드 타입 
    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 (Opertaion op : opSet)
            System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
    }

    열거 타입에서 인터페이스를 이용해 확장 하는 경우 사소한 문제점

    • 열거 타입끼리 구현을 상속할 수 없다.
    • 아무 상태에도 의존하지 않는 경우에는 디폴트 구현을 이용해 인터페이스에 추가하는 방법이 있다.

    자바 라이브러리 예시

    package java.nio.file;
    
    /**
     * Defines the options as to how symbolic links are handled.
     *
     * @since 1.7
     */
    
    public enum LinkOption implements OpenOption, CopyOption {
      /**
       * Do not follow symbolic links.
       *
       * @see Files#getFileAttributeView(Path,Class,LinkOption[])
       * @see Files#copy
       * @see SecureDirectoryStream#newByteChannel
       */
      NOFOLLOW_LINKS;
    }
    • OpenOption, CopyOption 을 구현한다.

    결론

    • 열거 타입 자체는 확장할 수 없지만, 인터페이스와 그 인터페이스를 구현하는 기본 열거 타입을 함께 사용해 같은 효과를 낼 수 있다.
    • API가 인터페이스 기반으로 작성되었다면 기본 열거 타입의 인스턴스가 쓰이는 모든 곳을 새로 확장한 열거 타입의 인스턴스로 대체해 사용할 수 있다.
킹수빈닷컴