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

Red Culture·2021년 5월 24일
0
  • 아이템 34: int 상수 대신 열거 타입을 사용하라
    -열거타입 (enum) : 일정 개수의 상수 값을 정의한 다음 그외의 값은 허용하지 않는 타입
    -정수 열거 패턴 (int enum pattern) :
     이전까지 사용하던 패턴
    -열거 타입 자체는 클래스이며, 상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개함
    -밖에서 접근할 수 있는 생성자를 제공하지 않기 때문에 사실상 final임. (열거 타입 선언으로 만들어진 인스턴스는 딱 하나만 존재함)
    -열거 타입을 선언한 클래스 혹은 그 패키지에서만 유용한 기능은 private이나 package-private 메서드로 구현한다. (구현된 열거 타입 상수는 자신을 선언한 클래스 혹은 패키지에서만 사용할 수 있는 기능을 담게됨)
    -구현한 기능을 클라이언트에 노출해야 할 합당한 이유가 없다면 private으로, 필요하다면 package-private으로 선언하라.
    -열거 타입은 톱레벨 클래스로 만들고, 특정 톱레벨 클래스에서만 쓰인다면 해당 클래스의 멤버 클래스로 만든다.
    -열거 타입 상수 일부가 같은 동작을 공유한다면 전략 열거 타입 패턴을 사용하자.
    → 전략 열거 타입 패턴

package effectivejava.chapter6.item34;

import static effectivejava.chapter6.item34.PayrollDay.PayType.*;

enum PayrollDay {
MONDAY(WEEKDAY), TUESDAY(WEEKDAY), WEDNESDAY(WEEKDAY),
THURSDAY(WEEKDAY), FRIDAY(WEEKDAY),
SATURDAY(WEEKEND), SUNDAY(WEEKEND);

private final PayType payType;

PayrollDay(PayType payType) { this.payType = payType; }

int pay(int minutesWorked, int payRate) {
    return payType.pay(minutesWorked, payRate);
}

enum PayType {
    WEEKDAY {
        int overtimePay(int minsWorked, int payRate) {
            return minsWorked <= MINS_PER_SHIFT ? 0 :
                    (minsWorked - MINS_PER_SHIFT) * payRate / 2;
        }
    },
    WEEKEND {
        int overtimePay(int minsWorked, int payRate) {
            return minsWorked * payRate / 2;
        }
    };

    abstract int overtimePay(int mins, int payRate);
    private static final int MINS_PER_SHIFT = 8 * 60;

    int pay(int minsWorked, int payRate) {
        int basePay = minsWorked * payRate;
        return basePay + overtimePay(minsWorked, payRate);
    }
}

public static void main(String[] args) {
    for (PayrollDay day : values())
        System.out.printf("%-10s%d%n", day, day.pay(8 * 60, 1));
}

}

  • 아이템 35: ordinal 메서드 대신 인스턴스 필드를 사용하라
    -ordinal 메서드**: 해당 상수가 그 열거 타입에서 몇 번째 위치하는지를 반환
    -상수 선언 순서를 바꾸는 순간 ordinal() 메서드를 리턴하는 메서드는 오 동작하게됨

  • 아이템 36: 비트 필드 대신 EnumSet을 사용하라
    -비트 필드: 비트별 OR를 사용해 여러 상수를 하나의 집합으로 모아서 만들어진 집합
    -비트 필드 사용시 문제점
    1) 정수 열거 상수의 단점 그대로 지님
    2) 해석이 어려움
    3) 모든 원소를 순회하기 까다로움
    4) API 작성 시 최대 몇 비트가 필요한지 예측하여 적절한 타입을 선택해야하는데 API를 수정하지 않고서는 비트 수(32비트 혹은 64비트)를 더 늘릴 수 없음
    -EnumSet 클래스는 열거 타입 상수의 값으로 구성된 집합을 효과적으로 표현해줌 (다른 어떤 Set 구현체와도 함께 사용할 수 있음)
    -EnumSet 클래스의 단점은 (자바9까지는) 불변 EnumSet을 만들 수 없다.

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

  • 아이템 38: 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
    -기본 열거 타입 대신 확장된 열거 타입을 넘겨 확장된 열거 타입의 원소 모두를 사용하게 할 수 있음
    -Class 객체 대신 한정적 와일드카드 타입을 넘기는 방법
    -인터페이스를 이용해 확장이 가능한 열거 타입을 흉내 내는 방식에도 문제가 있음
    → 열거 타입끼리 구현을 상속할 수 없음 → 아무 상태에도 의존하지 않는 경우에는 디폴트 구현을 이용해 인터페이스에 추가하는 방법이 있음

  • 아이템 39: 명명 패턴보다 애너테이션을 사용하라
    -명명 패턴의 단점: 오타가 나면 안되고 프로그램 요소에서만 사용되리라는 보증할 방법이 없음, 프로그램 요소를 매개변수로 전달할 방법이 없음
    -자바8에서는 여러 개의 값을 받는 애너테이션을 다른 방식으로도 만들 수 있음
    → 배열 매개변수를 사용하는 대신 애너테이션에 @Repeatable 메타애너테이션을 다는 방식
    1) @Repeatable을 단 애너테이션을 반환하는 컨테이너 애너테이션을 하나 더 정의하고 @Repeatable 에 이 컨테이너 애너테이션의 class 객체를 매개변수로 전달해야함
    2) 컨테이너 애너테이션은 내부 애너테이션 타입의 배열을 반환하는 value 메서드를 정의해야함
    3) 컨테이너 애너테이션 타입에는 적절한 보존 정책(@Retention)과 적용 대상(@Target)을 명시해야함

  • 아이템 40: @Override 애너테이션을 일관되게 사용하라
    -@Override는 메서드 선언에만 달 수 있으며, 상위 타입의 메서드를 재정의했음을 뜻함.
    -상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 에너테이션을 달자.
    → 단, 한가지 예외: 구체 클래스에서 상위 클래스의 추상 메서드를 재정의할 때는 굳이 @Override를 달지 않아도됨. (구체 클래스인데 아직 구현하지 않은 추상 메서드가 남아 있다면 컴파일러가 그 사실을 바로 알려주기 때문)
    -@Override는 클래스 뿐만 아니라 인터페이스의 메서드를 재정의할 때도 사용할 수 있음
    -추상 클래스나 인터페이스에서는 상위 클래스나 상위 인터페이스의 메서드를 재정의하는 모든 메서드에 @Override를 다는 것이 좋다.

  • 아이템 41: 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라
    -마커 인터페이스: 아무 메서드도 담지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스
    -마커 인터페이스가 마커 애너테이션보다 나은 점
    1) 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있음 (타입이기 때문에 런타임에야 발견될 오류를 컴파일타임에 잡을 수 있음)
    2) 적용 대상을 더 정밀하게 지정할 수 있음
    -새로 추가하는 메서드 없이 단지 타입 정의가 목적이라면 마커 인터페이스를 선택
    -클래스나 인터페이스의 외의 프로그램 요소에 마킹해야 하거나, 애너테이션을 적극 활용하는 프레임워크의 일부로 그 마커를 편입시키고자 한다면 마커 애너테이션이 올바른 선택임

profile
자기 개발, 학습 정리를 위한 블로그

0개의 댓글

관련 채용 정보