6장 — 열거형(enum)과 어노테이션

규칙 30 int 상수 대신 enum을 사용하라

  • 열거 자료헝(enumerated type)은 고정 개수의 상수들로 값이 구성되는 자료형이다. (객체, 배열)
  • 열거 상수 (enumeration constant)별로 하나의 객체를 public static final 필드 형태로 제공하는 것이다.
  • 상수를 제공하는 필드가 enum 자료형과 클라이언트 사이에서 격리 계층(layer of insulation) 구실
  • enum 자료형은 컴파일 시점 형 안전성(compile-time type safety)을 제공한다.
  • enum 자료형은 임의의 메서드나 필드도 추가할 수 있도록 한다.
  • 임의의 인터페이스를 구현할 수도 있다.
  • enum 자료형에는 object에 정의된 모든 고품질 메서드들이 포함되어 있으며 Completable 인터페이스와 Serializable 인터페이스가 구현되어 있다.
  • enum 상수의 직렬화 형식(serialized form)은 enum 자료형상의 변화 대부분을 견딜 수 있도록 설계되어 있다.
  • enum 상수에 데이터를 넣으려면 객체 필드(instance field)를 선언하고 생성자를 통해 받은 데이터를 그 필드에 저장하면 된다.
  • 외부(external) enum 자료형 상수별로 달리 동작하는 코드를 만들어야 할 때는 enum 상수에 switch 문을 적용하면 좋다.
  • enum 사용
    • 고정된 상수 집합이 필요할 때
    • 원래 열거형인 자료형(natural enumerated type)
    • 컴파일 시점에 모든 가능한 값의 목록을 알 수 있는 집합

규칙 31 ordinal 대신 객체 필드를 사용하라

// ordinal을 남용한 사례 - 따라하면 곤란
public enum Ensemble {
	SOLO, DUET, TRIO, QUARTET, QUINTET, SEXTET, SEPTET, OCTET, NONET, DECTET;

	public int numberOfMusicians() {
		return ordinal() + 1;
	}
}
  • enum 상수에 연계되는 값을 ordinal을 사용해 표현하지 말라는 것이다. 그런 값이 필요하다면 그 대신 객체 필드(instance field)에 저장해야 한다.

규칙 32 비트 필드(bit field) 대신 EnumSet을 사용하라

  • 집합을 비트 필드로 나타내면 비트 단위 산술 연산(bitwise arithmetic)을 통해 합집합이나 교집합 등의 집합 연산도 효율적으로 실행할 수 있다.
  • 열거 자료형을 집합에 사용해야 한다고 해서 비트 필드로 표현하면 곤란하다
  • RegularEnumSet, JumboEnumSet
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
 public boolean add(E e) {
        typeCheck(e);

        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }
}

규칙 33 ordinal을 배열 첨자로 사용하는 대신 EnumMap을 이용하라

  • ordinal 값을 배열 첨자로 사용하는 것은 적절치 않다는 것이다. 대신 EnumMap을 써라.

규칙 34 확장 가능한 enum을 만들어야 한다면 인터페이스를 이용하라

  • 계승 가능 enum 자료형은 만들 수 없지만, 인터페이스를 만들고 그 인터페이스를 구현하는 기본 enum 자료형을 만들면 계승 가능 enum 자료형을 흉내 낼 수 있다.

규칙 35 작명 패턴 대신 어노테이션을 사용하라

  • 작명 패턴(naming pattern)
    • 철자를 틀리면 알아채기 힘든 문제가 생긴다.
    • 특정한 프로그램 요소에만 적용되도록 만들 수 없다는 것이다.
    • 프로그램 요소에 인자를 전달할 마땅한 방법이 없다는 것이다.
// 표식 어노테이션 자료형(marker annotation type) 선언
import java.lang.annotation.*;

/**
* 어노데이션이 붙은 메서드가 데스트 메서드임을 표시.
* 무인자(parameterless) 정적 메서드에만 사용 가능
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD )
public @interface Test {
}
  • @Retention(RetentionPolicy.RUNTIME)은 Test가 실행시간(runtime)에도 유지되어야 하는 어노테이션이라는 뜻이다.
  • @Target(ElementType.METHOD)는 Test가 메서드 선언부에서만 적용할 수 있는 어노테이션이라는 뜻이다.
  • 어노테이션이 있으므로 더 이상은 작명 패턴에 기대면 안된다.
  • 대부분의 프로그래머는, 도구 개발에 관심 있는 개발자가 아니라면, 어노테이션 자료형을 정의할 필요가 없다.

규칙 36 Override 어노테이션은 일관되게 사용하라

  • 상위 클래스에 선언된 메서드를 재정의할 때는 반드시 선언부에 Override 어노테이션을 붇어야 한다.
  • 비-abstract 클래스에서 abstract 메서드를 재정의할 때는 Override 어노테이션을 붙이지 않아도 된다.

규칙 37 자료형을 정의할 때 표식 인터페이스를 사용하라

  • 표식 인터페이스(marker interface)는 아무 메서듣 선언하지 않는 인터페이스다.
  • 표식 인터페이스를 구현하는 것은, 해당 클래스가 어떤 속성을 만족한다는 사실을 표시하는 것과 같다.
  • 표식 인터페이스 장점
    • 가장 중요한 첫 번째 장점은, 표식 인터페이스는 결국 표식 붙은 클래스가 만드는 객체들이 구현하는 자료형이라는 점이다. 표식 어노테이션은 자료형이 아니다.
    • 표식 인터페이스가 어노테이션보다 나은 점 두 번째는, 적용 범위를 좀 더 세밀하게 지정할 수 있다는 것이다.
    • 표식 인터페이스는 자료형이므로, 표식 어노테이션을 쓴다면 프로그램 실행 중에나 발견하게 될 오류를 컴파일 시점에 발견할 수 있도록 한다.
  • 표식 어노테이션의 주된 장점은, 프로그램 안에서 어노테이션 자료형을 쓰기 시작한 뒤에도 더 많은 정보를 추가할 수 있다는 것이다.
  • 표식 어노테이션은 더 큰 어노테이션 기능의 일부라는 장점도 갖는다.
  • 만일 ElementType.TYPE에 적용될 표식 어노데이션 자료형을 작성하고 있다면, 반드시 어노테이션 자료형으로 구현해야 하는지, 표식 인터페이스로 만드는 것이 바람직하지는 않은지 고민해보기 바란다.