제어자처럼 사용.
애너테이션 자체로는 아무것도 하지 않으며, 도구가 있어야 유용하게 사용 가능
키/값 쌍 요소
@Test(timeout=10000)
애너테이션 요소 다음 중 하나
애너테이션 요소 기본값 가질 수 있음.
요소 이름이 value이고 이 요소만 지정할 때는 value= 생략 가능.
@SuppressWarnings("unchecked")
// 배열일 때 중괄호. 하나만 있으면 생략 가능
@BugReport(reportedBy={"Harry", "Fred"})
// 애너테이션 요소로 다른 애너테이션 사용 가능
@BugReport(ref=@Reference(id=112124), ...)
여러 애너테이션 가능
애너테이션이 반복가능으로 설정되었으면 같은 애너테이션 반복 가능.
@Entity
public class User {...}
@SupressWarnings("unchecked")
List<User> users = ...;
public class Cache<@Immutable V> {...}
@interface 문법을 사용해 애너테이션 인터페이스로 선언해야함.
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
long timeout();
...
}
@interface 선언은 실제 자바 인터페이스를 만들어냄.
애너테이션을 처리하는 도구는 애너테이션 인터페이스를 구현하는 객체를 받음.
@Target, @Retention 메타애너테이션
@Target 의 값은 ElementType 객체 배열. 애너테이션에 적용할 수 있는 아이템을 나타냄.
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface BugReport
@Retention : 애너테이션에 접근할 수 있는 위치를 지정.
RetentionPolicy.SOURCE: 애너테이션이 소스 핸들러에게는 보이지만 클래스 파일에는 포함되지 않는다
RetentionPolicy.CLASS: 애너테이션이 클래스 파일에 포함되지만 가상 머신은 해당 애너테이션을 로드하지 않는다. default
RetentionPolicy.RUNTIME: 애너테이션을 실행 시간에 이용할 수 있고 리플렉션 API를 통해 접근할 수 있다.
@SuppressWarnings
컴파일러의 특정 유형의 경고를 억제하게함
@SafeVarargs
대상 메서드가 가변 인자 파라미터에 손상을 주지 않는다고 단정
@Generated
도구로 생성한 소스 코드와 개발자가 작성한 코드를 구별할 때 붙인다.
@FunctionalInterface
람다 표현식의 변환 대상을 나타내는 데 사용
@PostConstruct, @PreDestroy
웹 컨테이너와 애플리케이션 서버처럼 객체의 생명주기를 제어하는 환경에서 사용
@Resource
리소스 주입
@Documented
자바독 같은 문서화 도구에 힌트를 제공.
문서화되는 애너테이션은 다른 문서화용 제어자(private, static 등)와 같은 식으로 취급해야 한다.
@Inherited
클래스용 애너테이션에만 적용. 클래스에 상속되는 애너테이션이 포함되면 서브클래스에서도 모두 자동으로 같은 애너테이션이 포함.
@Repeatable
같은 애너테이션을 여러 번 적용.
// inteface ToString
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ToString {
boolean includeName() default true;
}
@ToString(includeName=false)
public class Point {
@ToString(includeName=false)
private int x;
@ToString(includeName=false)
private int y;
}
@ToString
public class Rectangle {
@ToString(includeName=false) private Poion topLeft;
@ToString private int width;
@ToString private int height;
...
}
위 코드의 목적은 Rectangle 클래스 인스턴스를 Rectangle[[5,10],width=20,height=30]
와 같은 문자열로 표현하는 것이다.
핵심은 AnnotatedElement 인터페이스에 선언된 다음 메서드들이다.
T getAnnotation(Class<T>)
T getDeclaredAnnotation(Class<T>)
T[] getAnnotationsByType(Class<T>)
T[] getDeclaredAnnotationsByType(Class<T>)
Annotation[] getAnnotations()
Annotation[] getDeclaredAnnotations()
리플렉션 클래스인 Class, Field, Parameter, Method, Constructor, Package 가 AnnotatedElement 인터페이스를 구현한다.
public class ToStrings {
public static String toString(Object obj) {
if (obj == null) {
return null;
}
Class<?> cl = obj.getClass();
ToString ts = cl.getAnnotation(ToString.class);
// 객체에 @ToString 이 없으면 객체의 toString() 결과를 반환
if (ts == null) {
return obj.toString();
}
StringBuilder = result = new StringBuilder();
if (ts.includeName()) {
result.append(cl.getName());
}
result.append("[");
boolean isFirst = true;
// 객체의 필드들을 본다
for (Field f : cl.getDeclaredFields()) {
ts = f.getAnnotation(ToString.class);
if (ts != null) {
if (isFirst) {
isFirst = false;
} else {
result.append(",");
}
// private 필드도 접근 가능
f.setAccessable(true);
// includeName이 true이면
if (ts.includeName()) {
result.append(f.getName());
result.append("=");
}
try {
result.append(ToStrings.toString(f.get(obj)));
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
}
result.append("]");
return result.toString();
}
}