Study/Java

[Java] Annotation 이란?

꼽냥이 2021. 11. 3. 16:00
반응형

Annotation


자바를 사용하며 많은 어노테이션들을 코드에 적용하고 사용해 왔지만 정작 어노테이션이 어떤 의미를 갖는 것인지, 어떻게 사용해야 하고 어떤 식으로 개발자에게 도움을 줄 수 있는지를 몰라 이에 대해 정리를 해보려 한다.

 

우선 어노테이션의 사전적인 의미는 "주석" 이다. 일반적으로 생각하는 주석의 의미와는 사뭇 다른데, 보통 코드에서 주석의 역할은 소스 코드로 표현하기 어려운 개발자의 의도 등을 설명하기 위해 사용되는 것으로 알고 있다. 반면 어노테이션은 자바 코드에 추가될 수 있는 메타데이터라는 의미를 갖는다. 

 

어노테이션의 특징


  • 컴파일러에게 코드 문법 에러를 체크할 수 있도록 정보를 제공
  • 소프트웨어 개발 툴이 빌드나 배치 시 자동으로 코드를 생성할 수 있도록 정보 제공
  • 어노테이션을 만들 때는 용도를 명확하게 해야 한다.
    • 소스 상에서만 유지할 것인지
    • 컴파일된 클래스에서도 유지할 것인지
    • 런타임 시에도 유지할 것인지

 

Built-In Annotation


@Override

  • 메소드가 오버라이드 됐는지 검증
  • 부모 클래스나 구현해야 할 인터페이스에서 해당 메소드를 찾을 수 없을 경우 오류 발생

@Deprecated

  • 메소드를 사용하지 않도록 유도
  • 만약 위 어노테이션이 붙은 메소드를 사용할 경우 컴파일 시 경고를 발생

@SuppresWarnings

  • 컴파일 경고를 무시

@SafeVarargs

  • 제너릭과 같은 가변 인자를 사용할 때 경고를 무시 (Java 7 이상)

@FunctionalInterface

  • 람다 함수 등을 위한 인터페이스를 지정
  • 메소드가 없거나 두 개 이상일 경우 컴파일 오류 발생 (Java 8 이상)

 

Meta Annotations


위의 Built-In Annotation 같은 경우는, 기본적으로 자바에서 제공하는 어노테이션이다. 자바에서는 이 외에도 Meta Annotation 이라는 것을 지원하는데 이들을 활용해 커스텀 어노테이션을 만들고 사용하는 것 또한 지원한다.

 

@Retention

  • 어노테이션의 Life Time 을 의미
  • Class
    • 바이트 코드 파일까지 어노테이션 정보를 유지
    • 하지만 리플렉션을 이용해 어노테이션의 정보를 얻을 수는 없다
  • Runtime
    • 바이트 코드 파일까지 어노테이션 정보를 유지
    • 리플렉션을 이용해 런타임시 어노테이션의 정보를 얻을 수 있다
  • Source
    • 컴파일 이후 사라지는 형태
  • https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/RetentionPolicy.html

@Documented

  • 문서에도 어노테이션의 정보가 표현된다

@Target

  • 어노테이션을 적용할 위치를 결정한다

@Repeatable

  • 반복적으로 어노테이션의 선언을 가능케 한다

 

Annotation 구조


@Inherited // 상속
@Documented // 문서에 정보가 표현
@Retention(RetentionPolicy.RUNTIME) // 컴파일 이후에도 JVM에 의해서 참조가 가능합니다
@Retention(RetentionPolicy.CLASS)   // Compiler가 클래스를 참조할 때까지 유효합니다
@Retention(RetentionPolicy.SOURCE)  // 컴파일 이후 사라집니다
@Target({
		ElementType.PACKAGE, // 패키지 선언시
		ElementType.TYPE, // 타입 선언시
		ElementType.CONSTRUCTOR, // 생성자 선언시
		ElementType.FIELD, // 멤버 변수 선언시
		ElementType.METHOD, // 메소드 선언시
		ElementType.ANNOTATION_TYPE, // 어노테이션 타입 선언시
		ElementType.LOCAL_VARIABLE, // 지역 변수 선언시
		ElementType.PARAMETER, // 매개 변수 선언시
		ElementType.TYPE_PARAMETER, // 매개 변수 타입 선언시
		ElementType.TYPE_USE // 타입 사용시
})
public @interface NesoyAnnotation{
	/* enum 타입을 선언할 수 있습니다. */
	public enum Quality {
		BAD, GOOD, VERYGOOD
	}

	/* String은 기본 자료형은 아니지만 사용 가능합니다. */
	String value() default "NesoyAnnotation : Default String Value";

	/* 배열 형태로도 사용할 수 있습니다. */
	int[] values();

	/* enum 형태를 사용하는 방법입니다. */
	Quality quality() default Quality.GOOD;
}

 

Custom Annotation 만들어보기


Nyang Annotation

  • Annotation 은 Runtime 에 적용한다.
  • 어노테이션의 적용 범위는 Method 로 한다.
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,})
public @interface NyangAnnotation {
    String value() default "NyangAnnotation : Default String Value";
}

 

Nyang Object

class NyangObject {
    
    @NyangAnnoation(value = "I'm Annotation")
    public void annotationTest() {
        System.out.println("Hello! World!");
    }
}

 

ContextContainer

  • Java 의 Reflection 을 활용해 객체의 어노테이션을 Invoke 하고 Annotation 의 value 를 읽어오는 역할
class MyContextContainer {
	public MyContextContainer(){}
	/**
	 * 객체를 반환하기 전 어노테이션을 적용합니다.
	 * @param instance
	 * @param <T>
	 * @return
	 * @throws IllegalAccessException
	 */
	private <T> T invokeAnnonations(T instance) throws IllegalAccessException {
		Method[] methods = instance.getClass().getDeclaredMethods(); // Reflect으로 해당 클래스의 Method를 전부 조회합니다.

		for(Method method : methods){
			NyangAnnotation annotation = method.getAnnotation(NyangAnnotation.class); // Method들 중에 NyangAnnotation을 찾습니다.
			if(annotation != null) { // NesoyAnnotation이 존재한다면
				System.out.println(annotation.value()); // annotation의 value를 출력합니다.
			}
		}

		return instance;
	}

	/**
	 * 매개변수로 받은 클래스의 객체를 반환합니다.
	 * @param clazz
	 * @param <T>
	 * @return
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public <T> T get(Class clazz) throws IllegalAccessException, InstantiationException {
		T instance = (T) clazz.newInstance();
		instance = invokeAnnonations(instance);
		return instance;
	}
}

 

Main

public class Main {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		MyContextContainer demo = new MyContextContainer(); // Annotation을 호출할 Container를 선언합니다.
		NyangObject object = demo.get(NyangObject.class); // Container에서 클래스를 가져오면서 Annotation을 invoke합니다.
		object.annotationTest(); // Method를 호출합니다.
	}
}

 

결과

  • 기존 Method 에는 "Hello! World!" 만 출력하도록 했지만 어노테이션으로 인해 "I'm Annotation" 이 먼저 출력된다.

 

#Reference.

 

Java Annotation이란?

 

nesoy.github.io