티스토리 뷰
학습할 것 (필수)
- 애노테이션 정의하는 방법
- @retention
- @target
- @documented
- 애노테이션 프로세서
애노테이션(Annotation)이란? 정의하는 방법
애노테이션은 클래스나 메서드 등의 선언 시에 @를 사용하는 것을 말한다.
애노테이션은 메타데이터(metadata) 라고 볼 수 있다. 메타데이터란 애플리케이션이 처리해야 할 데이터가 아니라, 컴파일 과정에서 코드를 어떻게 컴파일하고 처리할 것인지를 알려주는 정보이다.
어노테이션은 다음 세 가지 용도로 사용된다.
- 컴파일러에게 코드 문법 에러를 체크하도록 정보를 알려주거나
- 컴파일할 때와 설치시의 작업을 지정하거나
- 실행할 때(런타임시) 별도의 처리가 필요할 때 사용한다.
자바의 표준 애노테이션 (Built-in 애노테이션)
@Override
- 메서드 앞에만 붙일 수 있는 애노테이션으로, 조상의 메서드를 오버라이딩하는 것이라는 걸 컴파일러에게 알려주는 역할
- 메서드를 오버라이딩할 때 필수는 아니지만 실수가 나올 수 있으므로 컴파일러가 알 수 있게 붙여주는 것을 권장한다.
class Parent {
void parentMethod() {
}
}
class Child extends Parent {
@Override
void parentMethod() {
super.parentMethod();
}
}
@ Deprecated
- 더 이상 사용하지 않는 필드나 메서드에 사용하는 애노테이션, 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다.
/*
* @deprecated As of JDK version 1.1,
* replaced by {@code Calendar.get(Calendar.DAY_OF_MONTH)}.
*/
@Deprecated
public int getDate() {
return normalize().getDayOfMonth();
}
@FunctionalInterface
- 자바8부터 사용할 수 있는 @FunctionalInterface는 함수형 인터페이스(functional interface)를 선언할 때, 이 애노테이션을 사용한다.
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
...
}
@SuppressWarnings
- 컴파일러가 보여주는 경고 메시지가 나타나지 않게 해 준다.
- 나타나지 않게 할 경고 메시지의 종류는 여러 개 있다. 주로 사용되는 것은 "deprecation", "unchecked", "rawtypes", "varargs"
- "deprecation"은 @Deprecated가 붙은 대상을 사용해서 발생하는 경고
- "unchecked"는 제네릭 타입으로 지정하지 않았을 때 발생하는 경고
- "rawtypes"는 제네릭을 사용하지 않아서 발생하는 경고
- "varargs"는 가변 인자의 타입이 제네릭 타입일 때 발생하는 경고
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
//단일로 사용
@SuppressWarnings("unchecekd")
//여러개 사용
@SuppressWarnings({"unchecekd","rawtypes", "varargs"})
@SafeVarargs
메타 애노테이션
메타 애노테이션이란 애노테이션에 붙이는 애노테이션으로 애노테이션을 정의할 때 애노테이션의 적용대상(target)이나 유지기간(retention)등을 지정하는 데 사용된다.
@Target
애노테이션이 적용 가능한 대상을 지정하는 데 사용된다.
자바 공부에서는 동떨어지는 이야기이지만 스프링 프레임워크의 @Controller 어노테이션
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
@Target으로 지정할 수 있는 애노테이션 적용대상의 종류
java.lang.annotation.ElementType이라는 열거형에 정의되어 있다.
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, //타입(클래스, 인터페이스, enum, 애노테이션)
/** Field declaration (includes enum constants) */
FIELD, //필드(멤버변수, enum상수)
/** Method declaration */
METHOD, //메소드
/** Formal parameter declaration */
PARAMETER, //매개변수
/** Constructor declaration */
CONSTRUCTOR, //생성자
/** Local variable declaration */
LOCAL_VARIABLE, //지역변수
/** Annotation type declaration */
ANNOTATION_TYPE, //애노테이션
/** Package declaration */
PACKAGE, //패키지
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, //타입 매개변수(1.8부터추가)
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE, //타입이 사용되는 모든 곳
/**
* Module declaration.
*
* @since 9
*/
MODULE
}
@Retention
애노테이션이 유지(retention)되는 기간을 지정하는 데 사용된다.
java.lang.annotation.RetentionPolicy 라는 열거형에 정의되어 있다.
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
//소스 파일에만 존재, 클래스파일에는 존재하지 않는다.
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
// 클래스 파일에 존재, 실행시에 사용 불가, 기본값이다.
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
//클래스 파일에 존재, 실행시에 사용가능하다.
RUNTIME
}
CLASS가 기본값이고 @Retention을 RUNTIME으로 하면 실행 시에 자바의 리플렉션(reflection)을 통해서 클래스 파일에 저장된 애노테이션의 정보를 읽어서 처리할 수 있다.
자바 8부터 추가된 @FunctionalInterface는 실행 시에도 사용되므로 유지 정책이 RUNTIME으로 되어 있다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
@Documented
@Documented는 애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다.
@Inherited
애노테이션이 자손 클래스에 상속되도록 한다.
@Inherited가 붙은 애노테이션을 조상 클래스에 붙이면, 자손 클래스도 이 애노테이션이 붙은 것과 같이 인식된다.
@Repeatable
보통은 하나의 대상에 한 종류의 애노테이션을 붙이는데 @Repeatable이 붙은 애노테이션은 여러 번 붙일 수 있다.
@Repeatable //ToDo 애노테이션을 여러 번 반복해서 쓸 수 있게 한다.
@interface ToDo{
String value();
}
@ToDo("delete test codes.")
@ToDo("override inherited methods")
class MyClass{
...
}
@Native
네이티브 메서드(native method)에 의해 참조되는 상수 필드(constant field)에 붙이는 애노테이션
애노테이션 만들어보기
애노테이션을 정의할 때는 @interface를 사용해서 정의한다. 그 뒤에 사용할 애노테이션 이름이 온다.
public @interface MyAnnotation {
String name() default "sskim";
int age() default 30;
}
애노테이션은 엘리먼트(element)를 멤버로 가질 수 있다. 각 엘리먼트는 타입과 이름으로 구성되어 있고, 디폴트 값을 가질 수 있다.
엘리먼트 이름을 name으로 선언하기 default 값을 주었다.
엘리먼트의 타입으로는 int, double, String, 열거 타입, Class 타입 그리고 이들의 배열 타입을 사용할 수 있다.
엘리먼트의 뒤에는 () 처럼 메서드 형식으로 작성해야 한다.
실행 클래스는 뭔가 좀 더 봐야 할 듯;;; 어렵네;;
애노테이션 프로세서
참조
자바의 정석 - www.yes24.com/Product/Goods/24259565?OzSrank=2
이것이 자바다 - www.yes24.com/Product/Goods/15651484
자바의 신 - www.yes24.com/Product/Goods/42643850
오라클 Docs - docs.oracle.com/javase/tutorial/java/index.html
라이브 강의
@Retention
SOURCE -> CLASS -> RUNTIME
SOURCE는 컴파일하고 난 후에는 애노테이션 자체가 없어진다. 대표적으로 @Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Override는 컴파일 이후에 바이트코드에는 있을 필요가 없기 때문에 SOURCE
CLASS는 바이트코드에 들어는 있는데 RUNTIME 이와는 다르게 리플렉션은 안된다. CLASS로 정의해 놓으면 클래스로드에서 읽어서 메모리에 적재하는데 메모리 읽어올 때 애노테이션 정보를 누락시킨다. 그래서 리플렉션이 안된다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "sskim";
int age() default 30;
}
public class App {
public static void main(String[] args) {
Annotation[] annotations = MyAnnotation.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
//결과
@java.lang.annotation.Documented()
@java.lang.annotation.Retention(value=RUNTIME)
@Inherited
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "sskim";
int age() default 30;
}
MyAnnotation 애노테이션을 쓴 MyHello 클래스
@MyAnnotation
public class MyHello {
private String privateName;
public String publicName;
}
MyHello 클래스를 상속받은 MyHelloChild 클래스
public class MyHelloChild extends MyHello {
}
getAnnotations()와 getDeclaredAnnotations()의 차이
getAnnotations()는 Inherited 된 애노테이션이 나타나는데 getDeclaredAnnotations는 그 안에 선언되어있는 거만 가져온다. 실제 선언된 애노테이션을 출력하기 때문에 아래의 결과 차이가 있다.
@MyAnnotation
public class App {
public static void main(String[] args) {
Annotation[] annotations = MyHelloChild.class.getAnnotations();
System.out.println("======getAnnotations()=======");
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
Annotation[] declaredAnnotations = MyHelloChild.class.getDeclaredAnnotations();
System.out.println("======getDeclaredAnnotations()=======");
for (Annotation declaredAnnotation : declaredAnnotations) {
System.out.println(declaredAnnotation);
}
}
}
======getAnnotations()=======
@week12.MyAnnotation(name="sskim", age=30)
======getDeclaredAnnotations()=======
Field 도 똑같다.
getFields() 는 private 말고 public 으로 선언된 것만 나온다.
getDgetDeclaredFields()는 클래스에 선언된! 것이 나온다 그래서 private 한 거도 나온다.
만약 MyHelloChild 클래스에 private 으로 선언된 변수가 있었으면 getDgetDeclaredFields() 했을 때도 출력이 된다.
@MyAnnotation
public class App {
public static void main(String[] args) {
Field[] fields = MyHelloChild.class.getFields();
System.out.println("======getFields()=======");
for (Field field : fields) {
System.out.println(field);
}
Field[] declaredFields = MyHelloChild.class.getDeclaredFields();
System.out.println("======getDeclaredFields()=======");
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
}
//결과
======getFields()=======
public java.lang.String week12.MyHello.publicName
======getDeclaredFields()=======
- Total
- Today
- Yesterday
- svn
- Github Status
- input
- docker
- maven
- mybatis config
- config-location
- Java
- Bash tab
- JavaScript
- 북리뷰
- Mac
- intellij
- 베리 심플
- mybatis
- elasticsearch
- rocky
- springboot
- k8s
- Spring Security
- LocalDateTime
- oracle
- Spring
- LocalDate
- localtime
- 오라클
- Kotlin
- Linux
- jQuery
- window
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |