책, 세미나, 컨퍼런스 후기/2022

Effective Java 3/E - 6장 열거 타입과 애너테이션 - 2

TwinParadox 2022. 3. 13. 21:57
728x90

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

명명 패턴의 단점

  • 오타가 나면 안 된다.
  • 올바른 프로그램 요소에서만 사용된다는 보장이 없다.
  • 프로그램 요소를 매개변수로 전달할 마땅한 방법이 없다.

 

 

애너테이션 선언

  • 일반적인 정의 방법
public @interface Sample {
}
  • JUnit에서 사용하는 @Test 애너테이션
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@Testable
public @interface Test {
}

 

 

메타 애너테이션

  • 애너테이션 선언에 다는 애너테이션

@Retention

  • 애너테이션이 언제까지 남아 있을지 결정하는 것으로, 일종의 라이프 사이클에 대한 정의
  • RetentionPolicy.SOURCE
    • 소스코드(.java)에 남아 있으며, 컴파일 과정에서 해당 정보가 사라진다.
    • Lombok의 @Getter, @Setter
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
	//...
}
  • RetentionPolicy.CLASS
    • 클래스 파일(.class), 바이트 코드에 남아 있으며, 런타임 시에는 유지되지 않는다.
    • Lombok의 @NonNull
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}
  • RetentionPolicy.RUNTIME
    • 런타임까지 남아있어서, Reflection으로도 정보 조회가 가능하다.
    • Spring의 @Controller, @Service, @Autowired
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

 

@Target

  • 어느 선언부에서 사용되어야 하는지를 명시
  • java.lang.annotation.ElementType enum에 명시된 내용을 바탕으로 적용 범위 지정 가능
package java.lang.annotation;

public enum ElementType {
    TYPE, // 클래스, 인터페이스, Enum 선언시
    FIELD, // 멤버 변수 선언시
    METHOD, // 메서드 선언시
    PARAMETER, // 매개변수 선언시
    CONSTRUCTOR, // 생성자 선언시
    LOCAL_VARIABLE, // 지역 변수 선언시
    ANNOTATION_TYPE, // 애너테이션 선언시
    PACKAGE, // 패키지 선언시
    TYPE_PARAMETER, // 매개 변수 타입 선언시
    TYPE_USE, // 타입 사용시
    MODULE; // 모듈 선언시

    private ElementType() {
    }
}

 

@Documented

  • 문서에도 애너테이션 정보 표시

 

@Inherited

  • 자식 클래스가 애너테이션 상속 가능

 

@Repeatable

  • 반복적으로 애너테이션 선언 가능
  • @Repeatable을 달게 되면, 이를 반환하는 컨테이너 애너테이션이 필요하다.
  • 컨테이너 애너테이션은 내부 애너테이션 타입의 배열을 반환하는 value 메서드를 정의해야 한다.
  • 컨테이너 애너테이션 타입에는 @Retention과 @Target을 명시해야 한다.

 

 

 

 

마커 애너테이션

  • 아무 매개변수 없이, 단순히 대상에 마킹하는 용도
  • 이름에 오타를 내거나, 메서드 선언 외의 프로그램 요소에 달면 컴파일 에러 발생

 

Reference

 

 

 

40. @Override 애너테이션을 일관되게 사용하라

상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override를 달아야 한다.

  • 해당 애너테이션은, 상위 타입의 메서드를 재정의했음을 의미한다.
  • 명시적으로 Overriding을 나타낸다.
  • IDE에서 Overriding이 잘 됐는지 아닌지도 사전에 알 수 있어 일관적으로 사용해주는 것이 좋다.
  • 클래스뿐 아니라 인터페이스의 메서드를 재정의할 때도 사용할 수 있다.
  • 추상 클래스나 인터페이스에서는 상위 클래스, 인터페이스의 메서드를 재정의하려는 모든 메서드에 달자.

 

 

41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라

마커 인터페이스(Marker Interface)

  • 어떠한 메서드도 없으면서, 자신을 구현하려는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스
  • Serializable
package java.io;

// 이 인터페이스가 있으면 직렬화가 가능함을 의미한다.
public interface Serializable {
}

 

 

마커 인터페이스 vs 마커 애너테이션

  • 마커 인터페이스가 마커 애너테이션보다 나은 점
    • 마커 인터페이스를 구현한 클래스의 인스턴스들을 구분하는 타입으로 사용할 수 있다.
    • 적용 대상을 좀 더 정밀하게 지정할 수 있다.
  • 마커 애너테이션이 마커 인터페이스보다 나은 점
    • 애너테이션 시스템의 지원을 받는다.

 

 

둘 중 어떤 것을 어떻게 사용하는 것이 좋은가?

  • 클래스와 인터페이스 외의 프로그램 요소를 마킹하는 경우는 당연히 애너테이션을 쓴다.
  • 당연한 이야기지만, 일관성을 위해서 애너테이션을 적극적으로 활용하는 프레임워크를 쓴다면, 애너테이션을 쓴다.
  • 마킹된 객체를 매개변수로 받는 메서드를 작성할 일이 있다면, 마커 인터페이스를 쓴다.
  • 적용 대상이 ElementType.TYPE인 마커 애너테이션에 대해서는 한 번쯤 마커 인터페이스가 될 수 있는지 고민하자.

 

Reference

 

 

728x90