📌 6장 : 열거 타입과 애너테이션

아이템 34. int 상수 대신 열거 타입을 사용하라

  • 정수 열거 패턴 단점
    • 타입 안전을 보장할 방법이 없고, 표현력이 좋지 않다.
    • 문자열 출력이 까다롭다.
  • 열거 타입(enum type)
    • 열거 타입 자체는 클래스이다.
    • 상수 하나당 자신의 인스턴스를 만들어 public static final 필드로 공개한다.
    • 밖에서 접근할 수 있는 생성자를 제공하지 않기 때문에 final
    • 열거 타입 선언으로 만들어진 인스턴스는 딱 하나씩 존재하는 것을 보장해준다.

아이템 35. ordinal 메서드 대신 인스턴스 필드를 사용하라

  • 열거 타입 상수는 하나의 정수값과 대응된다.
  • 모든 열거 타입은 해당 상수가 열거 타입에서 몇 번째 위치인지 ordinal이라는 메서드를 제공해준다.
  • 잘못된 사용 예시
    • 상수 선언 순서가 바뀌면 numberOfMusicians 오동작
    • 해결방법 : 열거 타입 상수에 연결된 값은 ordinal 메서드로 얻지 말고 인스턴스 필드에 저장하기
public enum Ensemble {
    SOLO, DUET, TRIO, QUARTET, QUINTET, 
    SEXTET, SEPTET, OCTET, NONET, DECTET;
    
    public int numberOfMusicians() { return ordinal() + 1; } 
}
public enum Ensemble {
    SOLO(l), DUET(2), TRI0(3), QUARTET(4), QUINTET(5), 
    SEXTET(6), SEPTET(7), 0CTET(8), D0UBLE_QUARTET(8), 
    N0NET(9), DECTET(10), TRIPLE_QUARTET(12);
    
    private final int numberOfMusicians;
    Ensemble(int size) { this.numberOfMusicians = size; }
    public int numberOfMusicians() { return numberOfMusicians; }
}​

아이템 36. 비트 필드 대신 EnumSet을 사용하라

  • 비트 필드(bit field)
    • 비트별 OR을 사용해 여러 상수를 하나의 집합으로 모을 수 있는 집합
    • 비트별 연산을 사용해 합집합과 교집합 같은 집합 연산을 효율적으로 수행
    • 정수 열거 상수의 단점을 갖고있다.
  • java.util 패키지의 EnumSet 클래스는 열거 타입의 상수 값으로 구성되 집합을 효과적으로 표현한다.
public class Text {
    public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
    
    // 어떤 Set을 넘겨도 되나, EnumSetoi 가장 좋다.
    public void applyStyles(Set<Style> styles) { ... } 
}​

아이템 37. ordinal 인덱싱 대신 EnumMap을 사용하라

  • 배열이나 리스트에서 원소를 꺼낼 때 ordinal 메서드로 인덱스를 얻는 경우가 있다.
  • 동작은 되지만 배열은 제네릭과 호환이 되지 않아 비검사 형변환을 수행해야 하고 깔끔하게 컴파일 되지 않는다.
  • EnumMap : 열거 타입을 키로 사용하도록 설계한 Map 구현체
Map<Plant.LifeCycle, Set<Plant» plantsByLifeCycle = 
  new EnumMapo(Plant.LifeCycle.class);
for (Plant.LifeCycle Ic : Plant.LifeCycle.values()) 
  plantsByLifeCycle.put(lc, new HashSeto());

for (Plant p : garden)
  plantsByLifeCycle. get (p.lifeCycle).add(p);
System.out.printIn(plantsByLifeCycle);
  • stream과 EnumMap 사용해서 데이터와 열거 타입 매핑
System.out.printIn(Arrays.stream(garden)
  .collect(groupingBy(p -> p.LifeCycle,
    () -> new EnumMap<>(LifeCycle.class), toSet())));​

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

  • 타입 안전 열거 패턴은 열거 타입과 다르게 확장할 수 있다.
  • 기본 연산 외에 사용자 확장 연산을 추가해야 하는 경우
    • 연산 코드용 인터페이스를 정의하고 열거 타입이 인터페이스를 구현하게 한다.
    • 하지만 열거 타입끼리 구현을 상속할 수 없다.
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;
    }
  }

아이템 39. 명명 패턴보다 애너테이션을 사용하라

  • 명명 패턴의 단점
    • 실수로 이름을 tsetSafety Override로 지으면 JUnit 3이 메서드를 무시하고 지나치고 테스트가 통과한 것으로 오해할 수 있다.
    • 올바른 프로그램 요소에서만 사용된다는 보증이 없다.
    • 프로그램 요소를 매개변수로 전달할 방법이 없다.
  • 애너테이션
    • JUnit 4에서 도입
    • marker 애너테이션 타입 선언 예시
import j;*ava.lang.annotation.*;

/**
* 테스트 메서드임을 선언하는 애너테이션이다. 
* 매개변수 없는 정적 메서드 전용이다.
*/
@Retention (Retent ionPol.icy .RUNTIME) 
@Target(ElementType.METHOD)
public @interface Test {
}
  • 메타애너테이션(meta-annotation) : 애터테이션 선언에 있는 애너테이션
    • @Test가 런타임에도 유지되어야 한다는 표시

아이템 40. @Override 애너테이션을 일관되게 사용하라

  • 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 달아야 한다.
  • 예외 : 구체 클래스에서 상위 클래스의 추상 메서드를 재정의 하는 경우

아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라

  • 마커 인터페이스(marker interface) : 아무 메서드를 담고 있지 않고, 자신을 구현하는 클래스가 특정 속성을 가진 것을 표현해주는 인터페이스
    • ex) Serializable : 자신을 구현한 클래스의 인스턴스를 직렬화(serialization)할 수 있도록 알려준다.
  • 마커 인터페이스와 마커 애너테이션
    • 마커 인터페이스는 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있다.
    • 마커 애너테이션은 그렇지 않다.
    • 마커 인터페이스는 적용 대상을 더 정밀하게 지정할 수 있다.
    • 마커 애너테이션은 거대한 애너테이션 시스템의 지원을 받는다.
    • 활용
      • 클래스와 인터페이스 외의 프로그램 요소에 마킹하는 경우 -> 마커 애너테이션 사용
      • 새로 추가하는 메서드 없이 타입 정의가 목적인 경우 -> 마커 인터페이스 사용
  • 자바의 직렬화
    • Serializable 마커 인터페이스를 보고 직렬화가 가능한 타입인지 확인

 

+ Recent posts