2021. 2. 5.

12주차 과제: 애노테이션

https://github.com/whiteship/live-study/issues/12

목표

자바의 애노테이션에 대해 학습하세요.

학습할 것 (필수)

  • 애노테이션 정의하는 방법
  • @retention
  • @target
  • @documented
  • 애노테이션 프로세서


애노테이션 정의하는 방법

// 골뱅이(@, at sign) 기호는 컴파일러에게 주석임을 알려줍니다.
// 예) @재정의
@Override
void mySuperMethod() { ... }

자바 애노테이션(Annotations)이란?

메타 데이터(metadata: 다른 데이터를 설명해 주는 데이터)의 한 형태인 주석(Annotations)은 프로그램의 일부는 아니지만, 프로그램에 대한 데이터를 제공합니다.

주석은 코드 작동에 직접적인 영향을 주지 않습니다.

주석은 다음과 같은 여러 용도가 있습니다.

  • 컴파일러에 대한 정보 - 컴파일러에서 주석을 사용하여 오류를 감지하거나 경고를 억제할 수 있습니다.
  • 컴파일 시간, 배포 시간 처리 - 주석 정보를 처리해 코드, XML 파일 등을 생성할 수 있습니다.
  • 런타임 처리 - 일부 주석은 런타임에 검사 가능
@SuppressWarnings (value = "unchecked") 
void myMethod () {...}

값이 하나만 있으면 다음과 같이 생략 가능합니다.

@SuppressWarnings ("unchecked")

주석 유형이 동일한 경우 반복 주석이라고 합니다.

@Author (name = "Kim") 
@Author (name = "Park") 
class MyClass {...}

반복 주석은 Java SE 8 릴리스부터 지원됩니다.

클래스, 필드, 메소드 및 기타 프로그램 요소 선언에도 주석을 사용할 수 있습니다.

자바 SE 8 릴리스부터 주석을 타입으로 적용할 수도 있습니다.

// 클래스 인스턴스 생성
new @Interned MyObject();

// 타입 캐스트
myString = (@NonNull String) str;

// 구현 절(implements clause)
class UnmodifiableList<T> implements
        @Readonly List<@Readonly T> { ... }

// 예외 처리
void monitorTemperature() throws
        @Critical TemperatureException { ... }

많은 애노테이션이 코드의 주석(코멘트)을 대체합니다.

소프트웨어 개발 그룹이 전통적으로 클래스 시작을 다음과 같이 중요한 정보를 제공하는 주석으로 시작한다고 가정해 봅시다.

public class MyClass extends MySuperClass {

   // Author: Tester1
   // Date: 2/01/2021
   // Current revision: 3
   // Last modified: 1/31/2021
   // By: Kim
   // Reviewers: Alice, Bill, Cindy

}

애노테이션을 사용해 똑같은 메타데이터를 추가합니다. 먼저 애노테이션 타입을 정의합니다.

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}

애노테이션 타입 정의는 인터페이스 정의와 비슷하게 interface 키워드 앞에 @를 붙입니다. 애노테이션 타입은 인터페이스의 한 형태입니다.

애노테이션 타입을 정의한 후 다음과 같이 값을 채워 타입으로 사용할 수 있습니다.

@ClassPreamble (
   author = "Tester1",
   date = "2/01/2021",
   currentRevision = 3,
   lastModified = "1/31/2021",
   lastModifiedBy = "Kim",
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class MyClass extends MySuperClass { ... }

참고: @ClassPreamble의 정보가 Javadoc 문서에 나타나도록 하려면 @ClassPreamble 정의에 @Documented 주석을 추가해야 합니다.

// @Documented 사용하기 위해 import
import java.lang.annotation.*;

@Documented
@interface ClassPreamble {
   // 애노테이션 요소 정의
}

출처:
https://docs.oracle.com/javase/tutorial/java/annotations/


@Retention

애노테이션 타입이 있는 애노테이션이 얼마나 보존될지를 나타냅니다. 타입 선언에 Retention 애노테이션이 없는 경우 retention 정책 기본값은 RetentionPolicy.CLASS 입니다.

  • RetentionPolicy.SOURCE - 소스 수준에서만 유지되며 컴파일러에서 무시됩니다.
  • RetentionPolicy.CLASS - 컴파일 타임에 컴파일러에 의해 유지되지만 JVM에서는 무시됩니다.
  • RetentionPolicy.RUNTIME - JVM에 의해 유지되므로 런타임 환경에서 사용할 수 있습니다.

Retention.java

package java.lang.annotation;

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.
 *
 * A Retention meta-annotation has effect only if the
 * meta-annotated type is used directly for annotation.  It has no
 * effect if the meta-annotated type is used as a member type in
 * another annotation type.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.4.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class MyTest {
    public static void main(String[] args) {
    
    @Retention(RetentionPolicy.RUNTIME)
//    @Retention(RetentionPolicy.CLASS)
//    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.METHOD)
    /* @Target({
            ElementType.PACKAGE, // 패키지 선언시
            ElementType.TYPE, // 타입 선언시
            ElementType.CONSTRUCTOR, // 생성자 선언시
            ElementType.FIELD, // 멤버 변수 선언시
            ElementType.METHOD, // 메소드 선언시
            ElementType.ANNOTATION_TYPE, // 어노테이션 타입 선언시
            ElementType.LOCAL_VARIABLE, // 지역 변수 선언시
            ElementType.PARAMETER, // 매개 변수 선언시
            ElementType.TYPE_PARAMETER, // 매개 변수 타입 선언시
            ElementType.TYPE_USE // 타입 사용시
    }) */
    public @interface MyAnnotation {
        /* enum 타입을 선언할 수 있습니다. */
        public enum Quality {BAD, GOOD, VERYGOOD}
        /* String은 기본 자료형은 아니지만 사용 가능합니다. */
        String value();
        /* 배열 형태로도 사용할 수 있습니다. */
        int[] values();
        /* enum 형태를 사용하는 방법입니다. */
        Quality quality() default Quality.GOOD;
    }
}

코드 출처: https://jdm.kr/blog/216

출처:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Retention.html

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Target.html

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/ElementType.html


@Target

애노테이션을 어디에 적용할 수 있는지 나타내는 컨텍스트입니다.

@Target 메타-애노테이션은 선언된 유형 자체가 메타 주석 유형임을 나타냅니다. 주석 유형 선언에서만 사용할 수 있습니다.

    @Target(ElementType.ANNOTATION_TYPE)
    public @interface MetaAnnotationType {
        ...
    }

이 @Target 메타 주석은 복잡한 주석 유형 선언에서 맴버 유형으로 사용하기위한 것입니다. 직접 주석을 다는데는 사용할 수 없습니다.

    @Target({})
    public @interface MemberType {
        ...
    }

ElementType 상수가 @Target 애노테이션에 두 번 이상 나타나는 것은 컴파일 타임 오류입니다.

    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
    public @interface Bogus {
        ...
    }

출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Target.html


@Documented

'Documented 주석 처리된 경우 javadoc과 같은 기본 도구는 출력시 해당 유형의 주석을 표시하지만 Documented가 없는 주석은 표시되지 않습니다.'

javadoc(소스 코드의 문서 주석을 HTML 형식으로 API 문서를 생성하는 도구)으로 api 문서를 만들 때 어노테이션에 대한 설명도 포함하도록 지정하는 메타테그

주석 포맷 설명: https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html#format

출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Documented.html


애노테이션 프로세서

애노테이션 프로세서는 컴파일 타임에 주석을 스캔하고 처리하기 위해 javac로 빌드된 도구입니다.

주석을 만들고 처리하는 단계

  • 주석 선언
  • 주석 프로세서 구현
  • 코드에서 주석 사용

AbstractProcessor

package com.example;

public class MyProcessor extends AbstractProcessor {

	@Override
	public synchronized void init(ProcessingEnvironment env){ }

	@Override
	public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }

	@Override
	public Set<String> getSupportedAnnotationTypes() { }

	@Override
	public SourceVersion getSupportedSourceVersion() { }

}
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

@SupportedAnnotationTypes("Todo")   // (1)
@SupportedSourceVersion(SourceVersion.RELEASE_8)   // (2)
public class TodoProcessor extends AbstractProcessor{   // (3)
    // (4)
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        
    }
}

(1) SupportedAnnotationTypes는 클래스가 처리할 주석의 유형을 알려주는 메타 주석입니다. 이 경우 클래스는 Todo 주석을 처리합니다.

(2) SupportedSourceVersion 이 주석 프로세서가 지원하는 최신 Java 버전입니다.

(3) 주석 프로세서는 Processor 인터페이스를 구현 하거나 AbstractProcessor 클래스를 확장해야 합니다.

(4) AbstractProcessor 클래스의 프로세스 메서드를 재정의 합니다 . 컴파일러는 응용 프로그램의 모든 클래스에서이 메서드를 호출합니다.

출처:
https://docs.oracle.com/en/java/javase/11/docs/api/java.compiler/javax/annotation/processing/AbstractProcessor.html

https://thetechstack.net/how-to-process-annotations-in-java/

https://www.baeldung.com/java-annotation-processing-builder

https://iammert.medium.com/annotation-processing-dont-repeat-yourself-generate-your-code-8425e60c6657

http://hannesdorfmann.com/annotation-processing/annotationprocessing101/

https://medium.com/@jason_kim/annotation-processing-101-%EB%B2%88%EC%97%AD-be333c7b913

댓글 없음:

댓글 쓰기