Annotation(이하 어노테이션)은 코드의 특정 구문이 무엇이고 어떻게 처리할지를 컴파일러 등에게 알리는 역할을 수행합니다. 어노테이션은 @메세지의 형태로 전달됩니다.
정확하게는 다음과 같은 세 가지 목적을 위해 사용됩니다.
- 컴파일 타임에서 사용되는 정보 전달
- 빌드 도구가 코드를 자동 생성할 때 사용되는 정보 전달
- 런타임에서 특정 기능을 처리하고자 할 때 사용되는 정보 전달
대표적으로 메소드 오버라이딩에서 사용했던 @Override가 현재로써는 가장 익숙한 어노테이션이라고 할 수 있죠.
이러한 어노테이션은 세 종류가 있습니다. 자바에서 기본적으로 제공하는 표준 어노테이션, 어노테이션 정의에 사용되는 메타 어노테이션, 사용자가 직접 정의하는 사용자 정의 어노테이션이 있습니다.
컴파일러에게 해당 메소드가 오버라이딩되었다고 알립니다.
해당 메소드 등을 더 이상 사용하지 않음을 알립니다. 만약 @Deprecated가 붙은 메소드를 사용하게 되면 오류가 발생합니다.
해당 인터페이스가 함수형 인터페이스임을 알립니다. 함수형 인터페이스는 Java 8부터 지원하는 문법입니다.
어노테이션이 선언된 위치에서 발생하는 컴파일 경고를 무시합니다.
Java 7 이상부터 사용가능한 어노테이션으로, 제네릭 등의 가변 인자의 매개변수를 사용할 때 발생하는 경고를 표시하지 않습니다.
어노테이션이 적용되는 대상을 지정합니다.
지정 대상은 ElementType 열거 상수를 통해 지정하는데, 이에 대해서는 잠시후 더 자세히 알아보도록 하겠습니다.
어노테이션 정보를 javadoc 문서에 포함시킵니다.
어노테이션이 영향을 주는 시점을 결정합니다.
마찬가지로 RetentionPolicy라는 열거 상수를 통해 지정하는데 이 역시도 잠시후에 살펴보겠습니다.
어노테이션이 하위 클래스에 상속될 지를 결정합니다.
어노테이션이 반복되어서 적용될 지를 결정합니다.
자바에서 제공하고있는 어노테이션들을 알아봤으니 이번에는 직접 어노테이션을 만드는 사용자 정의 어노테이션을 만들어보겠습니다.
어노테이션의 정의는 인터페이스의 정의와 유사합니다.
//정의
public @interface 어노테이션이름 {}
//사용할 때
@어노테이션이름
어노테이션은 속성을 가질 수 있습니다. 마치 필드를 선언하듯이 타입과 이름을 적는데, 이때 반드시 이름 뒤에 괄호를 붙여주어야합니다. default 키워드를 사용하면 속성에 기본값을 넣을 수 있습니다.
다음 예시는 속성이 있는 어노테이션을 만들고 사용하는 예시입니다.
public @interface 어노테이션이름 {
타입 속성명(); //속성 선언
타입 속성명() default 값; //기본값이 있는 속성
}
//사용할 때
@어노테이션이름(속성 = 값);
//속성이 여러개라면
@어노테이션이름(속성1 = 값, 속성2 = 값);
기본 속성으로 기본값이 없는 필수 속성인 value()를 사용할 수도 있습니다. value는 어노테이션을 사용할 때 따로 속성이름을 표시하지 않아도 됩니다.
public @interface 어노테이션이름 {
String value();
}
//사용할 때
@어노테이션이름("값");
이때 다른 속성과 value가 함께 사용된다면, value를 반드시 표기해주어야합니다.
public @interface 어노테이션이름 {
String value();
int num();
}
//사용할 때
@어노테이션이름(value = "값", num = 10);
@Target 어노테이션을 사용하면 정의된 어노테이션이 적용될 범위를 지정할 수 있습니다. 범위에 대한 정보는 ElementType 열거 상수로 정의되어있습니다.
| ElementType | 적용 범위 |
|---|---|
| TYPE | 클래스, 인터페이스, 열거형 |
| ANNOTATION_TYPE | 어노테이션 |
| FIELD | 필드 |
| CONSTRUCTOR | 생성자 |
| METHOD | 메소드 |
| LOCAL_VARIABLE | 지역 변수 |
| PACKAGE | 패키지 |
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
public @interface 어노테이션이름 {}
위와 같이 정의된 어노테이션은 클래스, 인터페이스, 열거형, 어노테이션에만 사용할 수 있게 됩니다.
@Retention을 사용하면 어노테이션이 언제 시작되어서 언제 종료될 것인지를 결정할 수 있습니다. 영향 범위도 마찬가지로 RetentionPolicy 열거 상수에 정의되어 있습니다.
| RetentionPolicy | 적용 시작 시점 | 적용 종료 시점 |
|---|---|---|
| SOURCE | 컴파일 과정 | 컴파일 후 |
| CLASS | 메모리 로딩 | 메모리 로딩 후 |
| RUNTIME | 실행중 | 계속 유지 |
그럼 위에서 알아본 어노테이션에 대한 정보를 토대로 사용자 정의 어노테이션을 만들어보겠습니다.
먼저 어노테이션 선언입니다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
어노테이션을 적용시킨 메소드를 가진 클래스입니다.
public class Obj {
@MyAnnotation("어노테이션 호출")
public void myMethod() {
System.out.println("사용자 정의 어노테이션 만들고 사용하기");
}
}
어노테이션이 적용되었는지 확인을 하고 어노테이션의 value()도 출력하는 main입니다. 지난 포스트에서 배운 리플렉션을 이용해 어노테이션 정보를 취득하고 이용합니다.
public class Main {
public static void main(String[] args) {
Method[] methods = Obj.class.getDeclaredMethods(); //Obj 클래스의 메소드들 취득
//해당 메소드에 어노테이션이 적용되었는지 if문으로 검사
if (methods[0].isAnnotationPresent(MyAnnotation.class)) {
//어노테이션이 있다면 어노테이션 정보 취득
MyAnnotation ma = methods[0].getAnnotation(MyAnnotation.class);
System.out.println("어노테이션이 적용되었습니다.");
System.out.println("어노테이션 value: " + ma.value()); //어노테이션 value() 출력
}
else {
System.out.println("어노테이션이 적용되지 않았습니다.");
}
}
}
