프로그램 코드처럼 사용할 수 있는 주석이라 이해하면 된다. 앞에 @가 붙어서 사용된다.
기존 CamelCase 방식이 아닌 다른 방식으로 메서드나 클래스를 불러야 할 필요가 있을 때 애노테이션을 사용한다.
애노테이션은 @interface를 만들어 사용한다.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 이 애노테이션이 적용될 대상(클래스, 메소드, 필드)을 지정합니다.
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 이 애노테이션 정보가 유지되는 기간(런타임까지)을 지정합니다.
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 문자열 타입 변수
String stringValue() default "default string";
// 정수 타입 변수
int intValue();
// 불리언 타입 변수
boolean booleanValue() default false;
// 문자열 배열 타입 변수
String[] arrayValue();
// 열거형 타입 변수
MyEnum enumValue();
// 다른 애노테이션 타입 변수
AnotherAnnotation annotationValue();
}
// 애노테이션 안에서 사용할 열거형
enum MyEnum {
VALUE1, VALUE2, VALUE3
}
// 애노테이션 안에서 사용할 또 다른 애노테이션
@interface AnotherAnnotation {
String description();
}
이렇게 해서 만들 수 있다. 애노테이션 안에 다양한 변수를 만들어 나중에 클래스 만들 때 @애노테이션(변수...)으로 붙일 수 있다.
@MyAnnotation(
intValue = 123,
arrayValue = {"hello", "java"},
enumValue = MyEnum.VALUE1,
annotationValue = @AnotherAnnotation(description = "This is another annotation")
)
public class MyClass {
@MyAnnotation(
intValue = 456,
stringValue = "field annotation",
arrayValue = {"field"},
enumValue = MyEnum.VALUE2,
annotationValue = @AnotherAnnotation(description = "Annotation for a field")
)
private String myField;
@MyAnnotation(
intValue = 789,
arrayValue = {"method"},
enumValue = MyEnum.VALUE3,
annotationValue = @AnotherAnnotation(description = "Annotation for a method")
)
public void myMethod() {
// ...
}
}
이렇게 해서 애노테이션에서 만들었던 변수에 값을 넣어 사용할 수 있다. 애노테이션에서 변수 뒤에 deafault 기본값으로 기본값 설정을 했으면 그 변수를 비워놔도 기본값으로 설정된다.
참고로 애노테이션에서 변수는 메서드 형태로 항상 빈 괄호가 있어야 하며, value라는 변수 명만 쓰일 경우 value라는 이름을 안 붙이고 값만 넣어 사용할 수 있다. 예를 들어 value 빼고 다 기본값이면 value의 값을 value도 안 쓰고 값을 넣을 수 있는 것이다.
반환 타입도 void가 아닌 구체적이어야 한다.
애노테이션 안에는 다른 애노테이션, 문자열, 열겨형, 배열, 클래스 등 다양한 타입의 변수가 사용될 수 있다.
여기서 @target은 애노테이션이 적용될 수 있는 대상이다. Method만 걸어놓고 필드에 적용하면 예외가 터진다. 주로 타입, 필드, 메서드를 사용한다.
@Retention은 애노테이션이 언제까지 살아있을지 정하는 것이다.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AnnotationReader {
public static void main(String[] args) throws Exception { // 예외 처리 간소화
// MyClass에 적용된 애노테이션 가져오기
MyAnnotation classAnnotation = MyClass.class.getAnnotation(MyAnnotation.class);
// 변수명으로 값 출력
System.out.println("Class intValue: " + classAnnotation.intValue()); // 출력: 123
System.out.println("Class stringValue: " + classAnnotation.stringValue()); // 출력: default string
System.out.println("Class enumValue: " + classAnnotation.enumValue()); // 출력: VALUE1
}
}
// myField 필드 객체 가져오기
Field field = MyClass.class.getDeclaredField("myField");
// 필드에 적용된 애노테이션 가져오기
MyAnnotation fieldAnnotation = field.getAnnotation(MyAnnotation.class);
// 변수명으로 값 출력
System.out.println("\nField intValue: " + fieldAnnotation.intValue()); // 출력: 456
System.out.println("Field stringValue: " + fieldAnnotation.stringValue()); // 출력: field annotation
// main 메소드 안에 이어서 작성
// myMethod 메소드 객체 가져오기
Method method = MyClass.class.getMethod("myMethod");
// 메소드에 적용된 애노테이션 가져오기
MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
// 변수명으로 값 출력
System.out.println("\nMethod intValue: " + methodAnnotation.intValue()); // 출력: 789
System.out.println("Method annotation description: " + methodAnnotation.annotationValue().description()); // 출력:
getAnnotaition으로 리플렉션을 이용해서 애노테이션을 사용할 수 있다.
target, Retention 외에도 @Documented, @Inherited가 있다.
@Documented는 자바 api 문서를 만들 때 사용되고, @Inherited는 애노테이션을 상속할 때 사용된다.
모든 애노테이션은 java.lang.annotation.Annotation 인터페이스를 묵시적으로 상속 받는다. 개발자가 직접 구현하거나 확장할 수는 없다.
Class<? extends Annotation> annotationType(): 애노테이션 타입을 반환한다.애노테이션에는 기본적으로 이러한 메서드들이 들어있다.
@Inherited를 사용해서 자식 클래스를 상속할 수 있다. 부모 클래스에 애노테이션을 붙이면 자식 클래스가 부모 클래스를 확장하여 애노테이션을 상속받는다. 인터페이스르 구현하는 방식으로는 안 된다. 인터페이스는 상속이 아닌 구현이고, 여러 인터페이스가 올 수 있어 인터페이스는 안 된다.