애너테이션이란, 주석처럼 프로그래밍 언어에 영향을 미치지는 않으면서도 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킴으로써 다른 프로그램에게 유용한 정보를 제공할 수 있는 기능을 제공하는 '메모'라고 할 수 있습니다. 이번 포스팅은 자바에서 제공하는 애너테이션에 대해서 알아보겠습니다.
자바에서 기본적으로 제공하는 애너테이션 중 대표적인 종류는 아래와 같습니다.
애너테이션 | 설명 |
@Override | 컴파일러에게 오버라이딩하는 메서드라는 것을 알려줍니다. |
@Deprecated | 앞으로 사용하지 않을 것을 권장하는 대상에 붙입니다. |
@SuppressWarnings | 컴파일러의 특정 경고메시지가 나타나지 않도록 해줍니다. |
@FunctionalInterface | 함수형 인터페이스임을 나타냅니다. |
그리고 자바에서 제공하는 애너테이션 중 '메타 애너테이션'이라는 것이 있습니다. 이 애너테이션은 애너테이션을 정의할 때 애너테이션 위에 붙이는 애너테이션, 즉 '애너테이션을 위한 애너테이션'입니다. 종류는 아래가 있습니다.
애너테이션 | 설명 |
@Target | 애너테이션이 적용가능한 대상을 지정하는데 사용합니다. |
@Documented | 애너테이션 정보가 javadoc으로 작성된 문서에 포함되도록 합니다. |
@Inherited | 애너테이션이 자손 클래스에 상속되도록 합니다. |
@Retention | 애너테이션이 유지되는 범위를 지정하는데 사용합니다. |
@Repeatable | 애너테이션을 반복해서 적용할 수 있게 합니다. |
우선 기본적인 표준 애너테이션부터 살펴보겠습니다.
표준 애너테이션
@Override
부모 메서드를 오버라이딩한 메서드임을 나타냄으로써 개발자가 메서드의 이름을 잘못 작성했거나 타입을 잘못 지정했을 경우 컴파일러가 오류를 잡아낼 수 있도록 도와줍니다.
class Person{
void printName(){
System.out.println("I'm Parent.");
}
}
class Child extends Person{
@Override
void printName(){
System.out.println("I'm a child.");
}
}
@Deprecated
새로운 버전의 JDK가 개발될 때 새로운 기능이 개발되면서 이전 기능들이 더 이상 필요하지 않은 경우가 있습니다. 하지만 이미 이 기능들이 다른 곳에서 사용되고 있을 수도 있기 때문에 함부로 삭제할 수 없습니다. 이런 경우 '앞으로는 이 기능을 사용하지 않는 것을 권장한다.'라는 의미를 나타내기 위해 @Deprecated 애너테이션을 사용합니다.
public class TestClass {
@Deprecated
int oldField;
@Deprecated
int getOldField(){
return oldField;
}
}
public class Main {
public static void main(String[] args) {
TestClass animal=new TestClass();
System.out.println(animal.getOldField());
}
}
@FunctionalInterface
함수형 인터페이스를 선언할 때 이 애너테이션을 붙이면 컴파일러가 함수형 인터페이스를 올바르게 선언했는지 확인하고 잘못된 경우 에러를 발생시킵니다. @Override처럼 이 애너테이션을 붙이면 실수를 방지할 수 있습니다.
// 자바에서 제공하는 Runnable 인터페이스
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@SuppressWarnings
컴파일러가 보여주는 경고 메시지를 억제해 주는 (나타나지 않게 해 주는) 역할을 하는 애너테이션입니다. 이 애너테이션으로 억제할 수 있는 경고는 여러 가지가 있지만 대표적으로는 'deprecation', 'unchecked', 'rawtypes', 'varags'가 있습니다.
- deprecation: @Deprecated가 붙은 대상을 사용해서 발생하는 경고
- unchecked: 제네릭(Generics)으로 타입을 지정하지 않았을 때 발생하는 경고
- rawtypes: 제네릭(Generics)을 사용하지 않아서 발생하는 경고
- varargs: 가변인자의 타입이 제네릭 타입일 때 발생하는 경고
아래와 같이 억제하고자 하는 경고 메시지를 애너테이션의 () 안에 지정할 수 있습니다. 여러 경고를 억제하고 싶을 때는 { } 안에 여러 개를 지정하면 됩니다.
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "unchecked"})
아래 예시를 보면 "deprecation"을 사용하면 원래는 노란 줄로 경고를 내던 컴파일러의 메시지가 없어지는 것을 확인할 수 있습니다.
JDK 버전이 올라가면서 원래는 없던 경고가 새로 생기는 경우도 발생합니다. 이때 새로 생긴 경고의 이름을 확인하려면 -Xlint 옵션으로 컴파일해서 경고의 내용 중 대괄호[ ] 안에 있는 경고 이름을 확인하여 @SuppressWarnings()에 값을 넣어서 적용할 수 있습니다.
두 번째로 메타 애너테이션에 대해서 간단히 살펴보겠습니다.
메타 애너테이션
@Target
애너테이션이 적용가능한 대상을 지정하는 데 사용됩니다.
// 자바에서 제공하는 SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
적용대상 종류
- ANNOTATION_TYPE: 애너테이션
- CONSTRUCTOR: 생성자
- FIELD: 필드(멤버변수, enum 상수)
- LOCAL_VARIABLE: 지역변수
- METHOD: 메서드
- PACKAGE: 패키지
- PARAMETER: 매개변수
- TYPE: 타입(클래스, 인터페이스, enum, 애너테이션)
- TYPE_PARAMETER: 타입 매개변수
- TYPE_USE: 타입이 사용되는 모든 곳
@Target({FIELD, TYPE, TYPE_USE})
public @interface TestTime {}
@TestTime // TYPE
class MyClass{
@TestTime
int hour; // FIELD
@TestTime
MyClass myClass; // TYPE_USE
}
@Retention
애너테이션이 유지되는 기간을 지정하는 데 사용됩니다.
유지 정책 | 의미 |
SOURCE | 소스 파일에만 존재. 클래스파일에는 존재하지 않음. |
CLASS | 클래스 파일에 존재. 실행시에 사용불가. 기본값 |
RUNTIME | 클래스 파일에 존재. 실행시에 사용가능. |
1. SOURCE
@Override나 @SuppressWarnings와 같은 컴파일러에 의해 사용되는 애너테이션은 'SOURCE' 정책을 사용합니다. 컴파일러를 직접 작성할 것이 아니라면 이 정책은 사용할 일이 없습니다.
2. CLASS
컴파일러가 애너테이션의 정보를 클래스 파일에 저장하지만, 클래스 파일이 JVM에 로딩될 때는 애너테이션의 정보가 무시되어 실행 시에는 애너테이션에 대한 정보를 얻을 수 없습니다. 그래서 보통 잘 사용하지 않습니다.
3. RUNTIME
실행 시에 클래스 파일에 저장된 애너테이션의 정보를 읽어서 처리할 수 있습니다. 보통 애너테이션을 개발자가 직접 정의하여 사용할 때 이 옵션을 사용합니다. 더 자세한 내용은 아래 포스팅에서 확인할 수 있습니다!
https://silver-programmer.tistory.com/m/entry/%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%95%A0%EB%84%88%ED%85%8C%EC%9D%B4%EC%85%98Annotation-%EC%A7%81%EC%A0%91-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0
@Inherited
애너테이션이 자손 클래스에 상속되도록 합니다.
@Inherited
public @interface InheritedAnno{}
@InheritedAnno
class Parent{}
class Child extends Parent{} // Child에도 InheritedAnno가 붙은 것으로 인식
@Repeatable
@Repeatable이 붙으면 해당 애너테이션을 한 대상에 여러 번 사용할 수 있습니다.
@Repeatable()
public @interface RepeatAnno{ String value();}
@RepeatAnno("repeat1")
@RepeatAnno("repeat2")
class Parent{}
[참고자료]
남궁 성, [Java의 정석 3rd Edition], 도우출판, 2016, p715~720
https://docs.oracle.com/javase/8/docs/technotes/guides/language/annotations.html
'Java' 카테고리의 다른 글
제네릭(Generic)이란? (0) | 2023.10.25 |
---|---|
Thread 구현과 실행 (Runnable vs Thread) (0) | 2023.10.25 |
자바에서 애너테이션(Annotation) 직접 정의하기 (0) | 2023.10.22 |
-Xlint 옵션 (0) | 2023.10.21 |
열거형(enums) (2) | 2023.10.21 |