KoreanFoodie's Study
6-3. 어노테이션(Annotation) : 자바 어노테이션 개념과 예제 본문
어노테이션
어노테이션은 메타데이터라고 볼수 있다.
@AnnotationName
어노테이션은 다음 세 가지 용도로 사용된다.
1. 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
2. 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
3. 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공
대표적으로는 @Override가 있다. (상속 메소드 재정의 시)
어노테이션 타입 정의와 적용
어노테이션 타입의 정의는 인터페이스를 정의하는 것과 유사하다. 다음과 같이 @interface를 사용해서 어노테이션을 정의하며, 그 뒤에 사용할 어노테이션 이름이 온다.
public @interface AnnotationName
{
타입 elementName() [default 값]; // 엘리먼트 선언
}
어노테이션은 엘리먼트를 멤버로 가질 수 있는데, 각 엘리먼트는 디폴트 값을 가질 수 있다. 타입으로는 기본형과 String, Enum, Class, 그리고 배열 타입을 사용할 수 있다. 엘리먼트의 이름 뒤에는 메소드를 작성하는 것처럼 ( )를 붙여야 한다.
public @interface AnnotationName
{
String elementName1();
int elementName2() default 5;
}
// 실제 적용
@AnnotationName(elementName1 = "값", elementName2=3);
// 또는
@AnnotationName(elementName1 = "값");
elementName1은 디폴트 값이 없기 때문에 반드시 값을 기술해야 하고, elementName2는 디폴트 값이 있기 때문에 생략 가능하다.
어노테이션 적용 대상
어노테이션을 적용할 수 있는 대상은 java.lang.annotation.ElementType 열거 상수이다.
어노테이션이 적용될 대상을 지정할 때는 @Target 어노테이션을 사용한다. 예를 들어 다음과 같이 어노테이션을 정의할 경우,
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface AnnotationName
{
}
클래스, 필드, 메소드만 어노테이션을 적용할 수 있고 생성자는 적용할 수 없다.
@AnnotationName
public class ClassName
{
@AnnotationName
private String fieldName;
//@AnnotationName (x) --- @Target에 CONSTRUCT가 없어 생성자는 적용 못함
public ClassName() { }
@AnnotationName
public void methodName() { }
}
어노테이션 유지 정책
어노테이션 정의시 사용 용도에 따라 어느 범위까지 유지할 것인지 지정해야 한다. 즉 소스상에만 유지하는지, 컴파일된 클래스까지 유지하는지, 런타임 시에도 유지할 것인지를 지정해야 한다. 어노테이션 유지 정책은 java.lang.annotation.RetentionPolicy 열거 상수로 다음과 같이 정의되어 있다.
리플렉션(Reflection)이란 런타임 시에 클래스의 메타 정보를 얻는 기능을 말한다. 예를 들어 클래스의 필드가 무엇인지, 어떤 생성자를 갖고 있는지, 어떤 메소드를 갖고 있는지, 적용된 어노테이션이 무엇인지 알아내는 것이 리플렉션이다.
리플렉션을 이용해서 런타임 시에 어노테이션 정보를 얻으려면 어노테이션 유지 정책을 RUNTIME으로 설정해야 한다.
@Retention의 기본 엘리먼트인 value는 RetentionPolicy 타입이므로 위 세가지 상수 중 하나를 지정하면 된다. (대부분 런타임 시점에 사용함)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName
{
}
런타임 시 어노테이션 정보 사용하기
런타임 시에 어노테이션이 적용되었는지 확인하고 엘리먼트 값을 이용해서 특정 작업을 수행하는 방법에 대해 알아보자. 리플렉션을 이용하면 어노테이션의 적용 여부와 엘리먼트 값을 읽고 적절히 처리할 수 있다! 클래스의 경우는 java.lang.Class를, 필드, 생성자, 메소드의 경우 java.lang.reflect 패키지를 사용한다.
각 메소드의 실행 내용을 구분선으로 분리해서 콘솔에 출력하는 어노테이션을 만들어 보자.
package annotation;
import java.lang.annotation.*;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation
{
String value() default "-";
int number() default 15;
}
다음은 PrintAnnotion을 적용한 Service 클래스이다.
package annotation;
public class Service
{
@PrintAnnotation
public void method1()
{
System.out.println("Execution 1");
}
@PrintAnnotation("*")
public void method2()
{
System.out.println("Execution 2");
}
@PrintAnnotation(value = "#", number = 20)
public void method3()
{
System.out.println("Execution 3");
}
}
이제 실제로 두 클래스를 받아 메소드를 호출해 보자.
package annotation;
import java.lang.reflect.Method;
public class PrintAnnotationExample
{
public static void main(String... args)
{
// Get Methods from Service Class
Method[] declaredMethods = Service.class.getDeclaredMethods();
// Take care of Method Objects, one by one
for(Method method :declaredMethods)
{
// Check if PrintAnnotation is applied
if (method.isAnnotationPresent(PrintAnnotation.class))
{
// Get PrintAnnotation Object
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
// Print method name
System.out.println("[" + method.getName() + "] ");
// Print lines
for (int i = 0; i < printAnnotation.number(); i++)
{
System.out.print(printAnnotation.value());
}
System.out.println();
try
{
method.invoke(new Service());
}
catch (Exception e) {}
System.out.println();
}
}
}
}
결과가 아래처럼 나오는 것을 확인해 볼 수 있다!
'Tutorials > Java' 카테고리의 다른 글
8-1. 자바 인터페이스의 역할과 선언, 구현 예제 살펴보기 (0) | 2021.09.15 |
---|---|
7. 자바의 상속 : 타입 변환과 다형성 (0) | 2021.09.15 |
6-2. 패키지 : 자바 패키지의 개념과 예제 (0) | 2021.09.14 |
6-1. 정적 멤버와 static : 정적 메소드, 정적 초기화 블록, 싱글톤 (0) | 2021.09.14 |
5. 자바 참조 타입 : 데이터 타입과 메모리 (0) | 2021.09.14 |