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) 적용 대상을 더 정밀하게 지정할 수 있음
-새로 추가하는 메서드 없이 단지 타입 정의가 목적이라면 마커 인터페이스를 선택
-클래스나 인터페이스의 외의 프로그램 요소에 마킹해야 하거나, 애너테이션을 적극 활용하는 프레임워크의 일부로 그 마커를 편입시키고자 한다면 마커 애너테이션이 올바른 선택임