자바에서 커스텀 어노테이션은 @interface로 선언하면 된다. 선언 시 필요한 범위에 따라 @Target, @Retention을 붙여줘야 한다. 아래와 같이 설정하면 실제로 많이 사용하는, 메소드에 붙여 동작을 제어할 수 있는 커스텀 어노테이션이 만들어진다.
// @Target({ElementType.METHOD, ElementType.TYPE}) // 여러 타입을 넣을 경우
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value();
}
이제 커스텀 어노테이션을 이루는 구성요소들에 대해 알아보자.
@Target - 어노테이션을 적용할 위치를 지정하는 어노테이션
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
위 코드는 주석을 제외한 1.8 기준의 코드인데, Target이 어노테이션 타입으로만 사용되는 어노테이션이기 때문에 ElementType.ANNOTATION_TYPE을 value로 가지고 있는 것을 확인할 수 있다. 만약 @Target을 메소드에 붙이면 '@Target' not applicable to method 이라는 경고 메시지를 확인할 수 있다. 클래스 등 다른 위치에 붙여도 마찬가지로 에러가 발생한다.
@Target은 java.lang.annotation.ElementType 타입의 enum 값을 리스트로 받는데, default가 없기 때문에 1개 이상의 ElementType을 지정해야 한다. ElementType의 종류는 아래와 같다.
- TYPE: Class, interface (annotation type 포함), enum에 선언
- FIELD: 필드 (enum의 constants도 포함)
- METHOD: 메소드
- PARAMETER: 파라미터
- CONSTRUCTOR: 생성자
- LOCAL_VARIABLE: 지역 변수
- ANNOTATION_TYPE: 어노테이션 타입
- PACKAGE: 패키지
아래 두 가지는 1.8에 추가된 항목이다. (참고링크)
- TYPE_PARAMETER: 타입 변수에 사용 가능
- generic class: MyClass<T> 의 T에 사용 가능 - MyClass<@CustomAnnotation T>
- generic method: <T> foo (...) {...} 의 T에 사용 가능 - <@CustomAnnotation T> foo (...) {...}
- wildcards: List<?>의 ?에 사용 가능 - List<@CustomAnnotation ?>
- TYPE_USE: 모든 종류의 타입에 사용 가능 (변수 선언시, generics, casts)
- @Target(ElementType.TYPE), @Target(ElementType.TYPE_PARAMETER) 두개를 대체할 수 있다. 즉, Class, interface, enum, type parameter 모두에 사용될 수 있다.
- 단, 타입에만 사용되기 때문에 아래와 같은 경우를 주의해야 한다.
@Target({TYPE_USE})
@interface TypeUseAnnotation {
}
@TypeUseAnnotation java.lang.Object illegalField; // illegal - field에 사용했기 때문
java.lang.@TypeUseAnnotation Object legalField; // legal - type에 사용했기 때문
@TypeUseAnnotation java.lang.Object illegalMethod () { ... } // illegal - method에 사용했기 때문
java.lang. @TypeUseAnnotation Object legalMethod () { ... } // legal - type에 사용했기 때문
@Retention - 자바 컴파일러가 어노테이션을 다루는 방법
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
어떤 시점까지 영향을 미치는지 결정하는 어노테이션으로, java.lang.annotation.RetentionPolicy 타입의 값을 받는다. RetentionPolicy의 종류는 아래와 같다.
- SOURCE: 컴파일 전까지만 유효하고 컴파일러에 의해 무시됨 - class 파일에 명시되지 않음
- CLASS: 컴파일까지는 유지되지만 실질적으로 런타임에는 사라짐 (디폴트 설정) - class 파일에 남아 있지만 runtime에는 버려짐
- RUNTIME: 런타임까지 사용할 수 있음
어노테이션의 body
body는 비워둬도 되고, 필요에 따라 필드, enum, list 등 다양한 값으로 구성할 수 있다. 이 부분은 필요에 따라 설정하면 된다.
단, 어노테이션의 내용으로 1가지만 정의하는 경우 관례적으로 명칭을 value()라고 사용하기 때문에 어노테이션의 내용에 value를 정의한 경우 prefix를 지정하지 않아도 자동으로 value로 처리가 된다.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnlyValueAnnotation {
String value();
}
@OnlyValueAnnotation("myannotation")
public void myMethod () {}
만약 value를 포함하여 다른 내용이 있을 경우 각각 key = value 로 값을 명시해주면 된다.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueAndDataAnnotation {
String value();
String data();
}
@ValueAndDataAnnotation("myAnnotation") // illegal - 'data' missing though required
public void myMethod () {}
@ValueAndDataAnnotation(value = "myAnnotation", data = "myData") // legal
public void myMethod2 () {}
value 외의 값에 디폴트 값을 지정해 두면 value 값만 넣어 사용할 수도 있다.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultDataAnnotation {
String value();
String data() default "defaultData";
}
@DefaultDataAnnotation("myAnnotation") // legal - data에 디폴트 값이 있기 때문
public void myMethod () {}
@DefaultDataAnnotation(value = "lolo", data = "newData") // legal - data에 다른 값을 넣을수도 있음
public void myMethod2 () {}
'Java' 카테고리의 다른 글
jackson의 두 라이브러리, codehaus vs fasterxml (0) | 2022.03.14 |
---|---|
Java.lang.StringBuilder (0) | 2022.03.14 |
AutoBoxing과 AutoUnboxing (0) | 2022.03.14 |
ArrayList와 LinkedList, 어떤 것을 사용해야 할까? (0) | 2022.03.14 |
Java에서의 여러가지 정렬(Sorting) (0) | 2022.03.14 |