본문 바로가기

개발 스토리/Java live study

Java 12주차 과제 : 애노테이션

목표

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


학습할 것

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

annotation이란?

애노테이션은 프로그램의 소스 코드 요소에 바인딩된 메타 데이터이며 애노테이션이 코드의 작동에는 영향을 주지 않습니다.

애노테이션은 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있다는 장점이 있습니다.

 

메타 데이터란?

데이터를 위한 데이터를 의미하며, 한 데이터에 대한 설명을 의미하는 데이터입니다.

 

일반적인 사용 사례

  • 애노테이션을 통해 컴파일러는 오류를 감지하거나 경고를 억제할 수 있습니다.
  • 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공합니다.
  • 실행 시 특정 기능을 실행하도록 정보를 제공합니다.

애노테이션 정의하는 방법

새로운 애노테이션을 정의하는 방법은 '@'기호를 붙이는 것을 제외하면 인터페이스를 정의하는 것과 동일합니다.

 

@interface 애노테이션이름 {
	타입 요소이름(); // 애노테이션의 요소를 선언한다.
}

애노테이션의 요소

애노테이션 내에 선언된 메서드를 '애노테이션의 요소(element)'라고 합니다. 참고로 애노테이션도 인터페이스처럼 상수를 정의할 수 있지만, 디폴트 메서드는 정의할 수 없습니다.

 

아래에 선언된 TestInfo애노테이션은 다섯 개의 요소를 갖는다.

 

@interface TestInfo {
	int count();
    String testedBy();
    String[] testTools();
    TestType testType();	// enum TestType{ FIRST, FINAL }
    DateTime testDate();	// 자신이 아닌 다른 애노테이션을 포함할 수 있다.
}

@interface DateTime {
	String yymmdd();
    String hhmmss();
}

애노테이션의 요소는 반환 값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며, 상속을 통해 구현하지 않아도 된다. 다만, 애노테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해주어야 한다. 요소의 이름도 같이 적어주므로 순서는 상관없습니다.

 

@TestInfo(
	count=3, testBy="Kim",
    testTools = {"JUnit", "AutoTester"},
    testType = TestType.FIRST,
    testDate = @DateTime(yymmdd="210131", hhmmss="230300")
)
public class NewClass { ... }

 

애노테이션의 각 요소는 기본값을 가질 수 있으며, 기본값이 있는 요소는 애노테이션을 적용할 때 값을 지정하지 않으면 기본값이 사용된다.

 

@interface TestInfo {
	int count() default 1;	// 기본값을 1로 지정
}

@TestInfo	// @TestInfo(count=1)과 동일
public class NewClass { ... }

 

애노테이션의 요소가 오직 하나뿐이고 이름이 value인 경우, 애노테이션을 적용할 때 요소의 이름을 생략하고 값만 적어도 됩니다.

 

@interface TestInfo {
	String value();
}

@TestInfo("passed")	// @TestInfo(value="passed")와 동일
class NewClass { ... }

 

요소의 타입이 배열인 경우, 괄호를 사용해서 어러 개의 값을 지정할 수 있다.

 

@interface TestInfo {
	String[] testTools();
}

@Test(testTools={"JUnit", "AutoTester"})	// 값이 여러 개인 경우
@Test(testTools="JUnit")	// 값이 하나일 때는 괄호생략가능
@Test(testTools={})	// 값이 없을 때는 괄호가 반드시 필요

 

기본값을 지정할 때도 마찬가지로 괄호를 사용할 수 있다.

 

@interface TestInfo {
	String[] info() default {"aaa","bbb"};	
    String[] info2() default "ccc";	// 기본값이 하나인 경우 괄호 생략가능.
}

@TestInfo	// @TestInfo(info={"aaa","bbb"}, info2="ccc") 와 동일
@TestInfo(info2={})	// @TestInfo(info={"aaa","bbb"}, info2={}) 와 동일
class NewClass { ... }

 

요소의 타입이 배열일 때도 요소의 이름이 value이면, 요소의 이름을 생략할 수 있다.

 

@interface SuppressWarnings {
	String[] value();
}

@SuppressWarnings({"deprecation", "unchecked"})
class NewClass { ... }

 

애노테이션 요소의 규칙

  • 요소의 타입은 기본형, String, enum, 애노테이션, Class만 허용된다.
  • () 안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다.
  • 요소를 타입 매개변수로 정의할 수 없다.
@interface AnnoTest {
	int id = 100;	// OK. 상수 선언. static final int id = 100;
    String major(int i, int j);	// 에러. 매개변수를 선언할 수 없음
    String minor() throws Exception;	// 에러. 예외를 선언할 수 없음
    ArrayList<T> list();	// 에러. 요소의 타입에 타입 매개변수 사용불가
}

@Retention

애노테이션이 유지(retention)되는 기간을 지정하는 데 사용된다. 애노테이션의 유지 정책(retention policy)의 종류는 다음과 같다.

 

유지 정책 의미
SOURCE 소스 파일에만 존재. 클래스파일에는 존재하지 않음.
CLASS 클래스 파일에 존재. 실행시에 사용불가. 기본값
RUNTIME 클래스 파일에 존재. 실행시에 사용가능.

 

'@Override'나 '@SuppressWarnings'처럼 컴파일러가 사용하는 애노테이션은 유지 정책이 'SOURCE'이다. 컴파일러를 직접 작성할 것이 아니면, 이 유지 정책은 필요 없다.

 

@Target(ElementType.METHOD)
@Retention(RetetionPolicy.SOURCE)
public @interface Override {}

 

유지 정책을 'RUNTIME'으로 하면, 실행 시에 '리플렉션(reflection)'을 통해 클래스 파일에 저장된 애노테이션의 정보를 읽어서 처리할 수 있다. '@FunctionalInterface'는 '@Override'처럼 컴파일러가 체크해주는 애노테이션이지만, 실행 시에도 사용되므로 유지 정책이 'RUNTIME'으로 되어 있다.

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

 

유지 정책 'CLASS'는 컴파일러가 애노테이션의 정보를 클래스 파일에 저장할 수 있게는 하지만, 클래스 파일이 JVM에 로딩될 때는 애노테이션의 정보가 무시되어 실행 시에 애노테이션에 대한 정보를 얻을 수 없다. 이것이 'CLASS'가 유지 정책의 기본값임에도 불구하고 잘 사용되지 않는 이유이다.


@Target

애노테이션이 적용 가능한 대상을 지정하는 데 사용된다.  '@Target'으로 지정할 수 있는 애노테이션 적용대상의 종류는 아래와 같다.

 

대상 타입 의미
ANNOTATION_TYPE 애노테이션
CONSTRUCTOR 생성자
FIELD 필드(멤버변수, enum상수)
LOCAL_VARIABLE 지역변수
METHOD 메서드
PACKAGE 패키지
PARAMETER 매개변수
TYPE 타입(클래스, 인터페이스, enum)
TYPE_PARAMETER 타입 매개변수(JDK1.8)
TYPE_USE 타입이 사용되는 모든 곳(JDK1.8)

 


@Documented

애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다. 자바에서 제공하는 기본 애노테이션 중에 '@Override'와 '@SuppressWarnings'를 제외하고는 모두 이 메타 애노테이션이 붙어 있다.

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

 


Annotation Processor란?

애노테이션을 이용해서 프로세스를 처리하는 것을 의미한다. 애노테이션을 사용하기 위해서는 애노테이션 프로세서가 필요하다. 특히 애노테이션 프로세서의 특징은 컴파일 단계에서 애노테이션에 정의된 액션을 처리하는데, 이는 우리의 애플리케이션이 실행되기 전 체킹을 해주기 때문에 애노테이션으로 의도한 대로 이루어지지 않을 경우 에러나 경고를 보여주기도 하며, 소스코드.java와 바이트코드.class를 생성해주기도 한다.

동작 구조.

1. 애노테이션 프로세서를 사용한다는 것을 자바 컴파일러가 알고 있는 상태에서 컴파일을 수행한다.

2. 애노테이션 프로세서들이 각자의 역할에 맞게 구현되어 있는 상태에서 실행되지 않은 애노테이션 프로세서를 실행한다.

3. 애노테이션 프로세서 내부에서 애노테이션에 대한 처리를 한다.

4. 자바 컴파일러가 모든 애노테이션 프로세서가 실행되었는지 검사하고, 모든 애노테이션 프로세서가 실행되지 않았다면 반복한다.

 

 

- 참고 자료

JAVA의 정석

webcoding-start.tistory.com/17

better-dev.netlify.app/java/2020/09/07/thejava_16/

im-recording-of-sw-studies.tistory.com/37