[이펙티브자바] 6장 열거 타입과 애너테이션(item34-41) 정리
아이템 34. int 상수 대신 열거 타입을 사용하라
- 정수 열거 패턴 단점 많다. 열거 타입을 쓰자!
- 열거 타입은 완전한 형태의 클래스. 상수 하나당 자신의 인스턴스를 하나씩 생성해 public static final 필드로 공개한다.
- 외부에서 접근 가능한 생성자를 제공하지 않는다.
- 열거 타입에는 임의의 메소드나 필드를 추가할 수 있고 임의의 인터페이스를 구현하게 할 수도 있다.
- 열거 타입은 불변이니까 모든 필드는 final 이어야 함!
- 필요한 원소를 컴파일타임에 다 알 수 있는 상수 집합이라면 항상 열거 타입 쓰자!
아이템 35. ordinal 메서드 대신 인스턴스 필드를 사용하라
- ordinal 메서드 사용하지 말고 인스턴스 필드에 저장하자!
- ordinal 메서드는 enumSet, enumMap 같은 열거 타입 기반 범용 자료구조에 쓸 목적으로 설계되었다. 그 외 용도라면 ordinal 메서드를 사용하지 말자
아이템 36. 비트 필드 대신 EnumSet을 사용하라
- 비트별 OR를 사용해 여러 상수를 하나의 집합으로 모은 것을 비트 필드라 한다.
- 비트 필드 단점
1. 정수 열거 상수의 단점을 그대로 가짐
2. 비트 필드값 그대로 노출되면 해석 어려움
3. 최대 몇 비트가 필요한지 처음부터 예상하여 적절한 타입을 선택해야함
- java.util.EnumSet을 사용하자. 장점
1. Set 인터페이스를 구현하여 어떤 Set 구현체와도 사용 가능, typeSafe
2. 내부가 비트 벡터로 구현되어 있어, 비트 필드에 비견되는 성능
- EnumSet은 불변 객체 못 만드는데, 만들고 싶으면 guava꺼 사용하기
Collections.unmodifiableSet() 으로 감싸서 사용하면 불변 가능
아이템 37. ordinal 인덱싱 대신 EnumMap을 사용하라
- Ordinal 쓰지말자. Ordinal 인덱싱 대신 EnumMap(enum 을 key 로 가짐)을 사용하라
- EnumMap은 내부에서 배열을 사용하기 때문에 ordinal 쓴 배열의 성능과 견줄 수 있다.
아이템 38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
- enum도 인터페이스를 구현할 수 있다.
- enum끼리는 구현을 상속할 수 없다.
https://www.baeldung.com/java-extending-enums
1. enum 은 java.lang.Enum 이라는 abstract class 의 하위클래스이다. enum A / enum B extends A 를 하면 B 는 java.lang.Enum 도 상속하고, A 도 상속하게 된다. → 다중상속은 자바에서 지원X
2. 컴파일 할 때 enum 을 final class 로 컴파일한다 (final class 는 extend 불가능)
아이템 39. 명명 패턴보다 에너테이션을 사용하라
- 명명 패턴이란 변수나 함수의 이름을 일관된 방식으로 작성하는 패턴을 말한다. 문제점이 많다. 사용자가 특히 주의해야함
- 애너테이션을 쓰면 문제점이 해결된다.
- 마커 어노테이션
아무 매개변수 없이 대상에 단순 마킹 용도
- 메타 어노테이션
@Retention
: 어디까지 살아있을지 (런타임, 클래스, 소스)
@Target
: 어노테이션이 붙을 대상 알려줌 (METHOD, TYPE, FIELD ..)
@Repeatable
같은 어노테이션 중복 정의 가능
이 어노테이션들의 묶음을 관리하는 컨테이너 어노테이션이 있어야함
이때 하위, 컨테이너 어노테이션 모두에 Retention, Target 잘 붙여주자
아이템 40. @Override 애너테이션을 일관되게 사용하라
@Override
애너테이션을 굳이 안붙여도 되는 상황이 있지만, 명확한 기준을 세우고 일관되게 사용하자.
- 항상 붙이거나 or 불필요한 곳엔 아예 안붙이거나
아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라
- 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을
instanceof
등으로 구분할 수 있다. (컴파일타임에)
마커 애너테이션을 사용했다면 clazz.isAnnotationPresent(Markable1.class)
등으로 인스턴스를 구분할 수는 있지만, 런타임에야 오류를 발견할 수 있다.
- 마커 인터페이스는 대상을 더 정밀하게 지정할 수 있다.
@Target
을 ElementType.TYPE
으로 지정해도 클래스, 인터페이스, enum, 애너테이션에 달 수 있다.
마커 인터페이스는 마킹하고 싶은 클래스에만 달 수 있다. → 하위 타입임이 보장됨
- 마킹이 된 객체를 매개변수로 받는 메서드를 작성할 일이 있다면 마커 인터페이스를 사용하자.