애너테이션이란, 주석처럼 프로그래밍 언어에 영향을 미치지는 않으면서도 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킴으로써 다른 프로그램에게 유용한 정보를 제공할 수 있는 기능을 제공하는 '메모'라고 할 수 있습니다.
Java에서는 @Override나 @SuppressWarnings와 같은 다양한 애너테이션을 제공합니다. 이번 시간에는 우리가 직접 애너테이션을 만들어서 어떻게 사용할 수 있는지에 대해서 알아보겠습니다.
애너테이션 정의
먼저, 애너테이션을 정의하는 방법은 아래와 같습니다.
@interface 애너테이션이름{
타입 요소이름 (); // 애너테이션 요소 선언
...
}
애너테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가집니다. 상속을 통해서 구현될 필요는 없습니다. 하지만, 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해주어야 합니다.
예시를 통해 확인해 보겠습니다.
1. 애너테이션 정의
애너테이션애너테이션 정의 후, 실제로 클래스에서 애너테이션 요소에 접근하고자 한다면 아래와 같이 @Retention(RententionPolicy.RUNTIME)으로 설정을 해주어야 합니다.
// TestClass.java
@Retention(RetentionPolicy.RUNTIME) // 실행시에 사용 가능하도록 지정!!
public @interface TestClass{
int version();
String author();
String[] coworker();
TestType testType(); // enum 타입
TestTime testTime(); // 애너테이션 타입
}
//TestType.java
public enum TestType{First(1), Second(2);
int i;
TestType(int i) {
this.i = i;
}
}
//TestTime.java
public @interface TestTime{
String yymmdd();
String hhmmss();
}
[참고] - @Retention
Java에서 제공하는 메타 애너테이션으로, '애너테이션을 위한 애너테이션'이라고 할 수 있습니다. 즉, 애너테이션에 붙이는 애너테이션으로, 애너테이션을 정의할 때 애너테이션의 적용대상(target)이나 유지기간(retention)등을 지정하는 데 사용됩니다.
<@Retention 정책 종류>
유지 정책 | 의미 |
SOURCE | 소스 파일에만 존재. 클래스파일에는 존재하지 않음. |
CLASS | 클래스 파일에 존재. 실행시에 사용불가. 기본값 |
RUNTIME | 클래스 파일에 존재. 실행시에 사용가능. |
2. 애너테이션 적용
아래 코드와 같이 여러 개의 요소를 가진 애너테이션이라면 각 요소의 이름을 적어주어야 합니다. (이름을 명시해주므로 순서는 상관없습니다.)
// AnnotationExample.java
@TestClass(version = 1,
author = "Silver Programmer",
coworker = {"Eun", "Kim"},
testType = TestType.First,
testTime = @TestTime(yymmdd="231021", hhmmss="155400"))
public class AnnotationExample {}
만약 하나의 요소를 가진 애너테이션이고, 그 요소의 이름이 value라면 애너테이션 적용 시, 요소의 이름을 적을 필요 없이 값만 적어주면 됩니다.
// 애너테이션 정의
public @interface Simple {
int value();
}
// 애너테이션 적용
@Simple(123)
class ... {
...
}
아래와 같이 배열도 가능합니다.
// 애너테이션 정의
public @interface Simple {
String[] value();
}
// 애너테이션 적용
@Simple("123")
class ...{
...
}
자바에서 제공하는 @SuppressWarnings 애너테이션이 위와 같이 String [] value(); 로만 요소가 정의되어 있어 애너테이션을 적용할 때 값을 생략할 수 있습니다.
// 자바에서 제공하는 SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
// 애너테이션 적용
@SuppressWarnings({"deprecation", "unchecked", "varags", "rawtypes"})
// @SuppressWarnings(value = {"deprecation", "unchecked", "varags", "rawtypes"})
또한, 배열로 선언된 요소라면 위의 코드처럼 { }로 여러 개의 값을 지정할 수 있습니다. 만약, 여러 개의 값을 지정할 필요가 없다면 { }는 생략 가능합니다. 마지막으로, 값이 없을 때는 { }가 반드시 필요합니다.
@TestClass(coworker = {"Eun", "Kim"}) // 값이 여러 개 일 때
@TestClass(coworker = "Eun") // 값이 한 개 일 때
@TestClass(coworker = {}) // 값이 없을 때
애너테이션의 각 요소는 기본값(디폴트 값)을 가질 수도 있습니다. 디폴트 값을 가지면 애너테이션 적용 시, 값을 지정하지 않아도 되고 지정하지 않으면 디폴트 값이 적용됩니다.
// TestClass.java
@Retention(RetentionPolicy.RUNTIME)
public @interface TestClass{
int version();
String author() default "Silver Programmer"; // 기본값
String[] coworker();
TestType testType() default TestType.First; // 기본값
TestTime testTime();
}
// TestType.java
public enum TestType{First(1), Second(2);
int i;
TestType(int i) {
this.i = i;
}
}
// TestTime.java
public @interface TestTime{
String yymmdd();
String hhmmss();
}
// AnnotationExample.java
// 애너테이션 적용시 author, testType 요소 지정은 생략 가능
@TestClass(version = 1,
// author = "Silver Programmer",
coworker = {"Eun"},
// testType = TestType.First,
testTime = @TestTime(yymmdd="231021", hhmmss="155400"))
public class AnnotationExample {}
마커 애너테이션
자바에서 제공하는 @Override처럼 요소가 필요 없는 경우 요소를 하나도 정의하지 않는 애너테이션을 마커 애너테이션이라고 합니다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
애너테이션 요소의 규칙
- 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용됩니다.
- ( ) 안에 매개변수를 선언할 수 없습니다.
- 예외를 선언할 수 없습니다.
- 요소를 타입 매개변수로 정의할 수 없습니다.
애너테이션의 조상 Annotation
모든 애너테이션의 조상은 java.lang.annotation.Annotation 입니다. 하지만 애너테이션은 상속이 불가하여 Annotation을 상속받도록 지정할 수 없습니다. 또한, Annotation은 애너테이션이 아니라 일반적인 interface 입니다. 따라서, 모든 애너테이션 객체들은 Annotation에서 제공하는 메서드들을 사용할 수 있습니다.
// java에서 제공하는 Annotation 인터페이스
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
애너테이션 사용하기
애너테이션을 직접 정의한 후 애너테이션의 요소 값을 출력하려면 아래와 같이 코드를 작성하면 됩니다.
Class<AnnotationExample> cls = AnnotationExample.class;
TestClass testAnno = cls.getAnnotation(TestClass.class);
System.out.println(testAnno.author());
위 getAnnotation()은 해당 클래스에 적용된 특정 애너테이션만 가지고 옵니다. 만약, 해당 클래스 객체에 적용된 모든 애너테이션을 가지고 오려면 getAnnotations() 메서드를 사용합니다.
Annotation[] annotations = cls.getAnnotations();
전체 코드로 확인하겠습니다.
// TestClass.java
@Retention(RetentionPolicy.RUNTIME)
public @interface TestClass {
int version();
String author() default "Silver Programmer";
String[] coworker();
TestType testType() default TestType.First;
TestTime testTime();
}
// TestType.java
public enum TestType {
First(1), Second(2);
int i;
TestType(int i) {
this.i = i;
}
}
// TestTime.java
// @Retention(RetentionPolicy.RUNTIME) 필요 없음
public @interface TestTime {
String yymmdd();
String hhmmss();
}
// Simple.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Simple {
String[] value();
}
// AnnotationExample.java
@TestClass(version = 1,
// author = "Silver Programmer",
coworker = {"Eun"},
// testType = TestType.First,
testTime = @TestTime(yymmdd = "231021", hhmmss = "155400"))
@Simple("123")
public class AnnotationExample {}
// Main.java
public class Main {
public static void main(String[] args) {
Class<AnnotationExample> cls = AnnotationExample.class;
TestClass testAnno = cls.getAnnotation(TestClass.class);
System.out.println("TestClass Author: " + testAnno.author());
System.out.println("TestClass testDate: "+testAnno.testTime().yymmdd());
System.out.println("TestClass testTime: "+testAnno.testTime().hhmmss());
System.out.println("===================================================");
Annotation[] annotations = cls.getAnnotations();
for (Annotation anno : annotations) {
System.out.println("Anootation toString(): " + anno.toString());
System.out.println("Annotation hashCode(): " + anno.hashCode());
System.out.println("Annotation annotationType(): " +anno.annotationType());
System.out.println("------------------------------------------------------");
}
}
}
[결과]
TestClass Author: Silver Programmer
TestClass testDate: 231021
TestClass testTime: 155400
===================================================
Anootation toString(): @com.example.demo.TestClass(testType=First, author="Silver Programmer", version=1, coworker={"Eun"}, testTime=@com.example.demo.TestTime(yymmdd="231021", hhmmss="155400")) Annotation hashCode(): -786566109
Annotation annotationType(): interface com.example.demo.TestClass
------------------------------------------------------
Anootation toString(): @com.example.demo.Simple({"123"})
Annotation hashCode(): 1335662942
Annotation annotationType(): interface com.example.demo.Simple
------------------------------------------------------
[참고자료]
남궁 성, [Java의 정석 3rd Edition], 도우출판, 2016, p702~714
https://docs.oracle.com/javase/8/docs/technotes/guides/language/annotations.html
'Java' 카테고리의 다른 글
Thread 구현과 실행 (Runnable vs Thread) (0) | 2023.10.25 |
---|---|
자바의 애너테이션(Annotation) (2) | 2023.10.22 |
-Xlint 옵션 (0) | 2023.10.21 |
열거형(enums) (2) | 2023.10.21 |
Comparable과 Comparator (4) | 2023.10.20 |