Java

[Effective Java] 6-2. 열거 타입과 애노테이션

noahkim_ 2024. 12. 31. 02:15

조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.


1. ordinal 인덱싱 대신 EnumMap을 사용하라

EnumMap

  • 열거 타입을 키로 사용하도록 설계한 Map 구현체
  • 데이터와 열거타입을 매핑

 

장점 (vs 배열)
  • 효율성
    • 내부에서 배열을 사용
  • 타입 안전성
    • 생성자에 키에 대한 클래스 리터럴(한정적 타입 토큰) 전달
    • 런타임 제네릭 타입 정보 제공
  • 인덱스 계산 오류 X
    • 내부적으로 인덱스 범위와 계산을 처리해줌스트

 

Stream 사용

그룹화
Arrays.stream(garden)                               // garden 배열을 스트림으로 변환
    .collect(groupingBy(                            // groupingBy를 사용해 그룹화
        p -> p.lifecycle,                           // 각 식물 p의 lifecycle을 기준으로 그룹화
        () -> new EnumMap<>(LifeCycle.class),      // 결과를 EnumMap으로 저장, 키는 LifeCycle enum
        toSet()                                    // 각 그룹의 값을 Set으로 저장
    ));
  • lifecycle에 속하는 식물이 있는 경우에만 키가 생성됨

 

중첩

public enum Phase {
    SOLID, LIQUID, GAS;
    
    public enum Transition {
        MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);
        
        private final Phase from, to;
        
        Transition(Phase from, Phase to) {
            this.from = from;
            this.to = to;
        }
        
        private static final Map<Phase, Map<Phase, Transition>> m =
            Stream.of(values()).collect(
            	groupingBy(t -> t.from,
            	() -> new EnumMap<>(Phase.class),
            	toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase.class))
            ));
        
      	public static Transition from(Phase from, Phase to) {
        	return m.get(from).get(to);
        } 
    }
}

 

  • 타입안전성
  • 확장성

 

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

  • 기존 열거형을 확장하는 방식
  • 새로운 상수에 대한 동작을 정의하여 유연성을 증가시킬 수 있음

 

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

명명 패턴 단점

오타가 나면 안됨
올바른 곳에서 사용되리라 보증할 방법 없음
  • 개발자에게 의존하는 경우가 많음

 

매개변수로 전달할 마땅한 방법이 없음
  • 규칙에 맞는지 확인할 방법 없음

 

애너테이션

  • 프로그램에게 추가 정보를 제공
  • 대상 코드의 의미는 그대로

 

Method.isAnnotationPresent
for (Method m : clazz.getDeclaredMethods()) {
    if (m.isAnnotationPresent(Test.class) {
        // 작업 수행       
    }
}
  • 메서드에 특정 타입의 애노테이션이 붙어있는지 확인
  • 해당 에노테이션에 관심있는 처리기에서 작업을 수행할 기회를 줌

 

사용자 애노테이션

 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    Class<? extends Throwable> value();
}
  • 모든 Exception 타입을 수용