참고
자바의 정석
프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이 바로 애너테이션이다. 애너테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있다.
애너테이션은 JDK에서 기본적으로 제공하는 것과 프로그램에서 제공하는 것들이 있는데 어느 것이든 그저 약속된 형식으로 정보를 제공하기만 하면 될 뿐이다.
JDK에서 제공하는 표준 애너테이션은 주로 컴파일러를 위한 것으로 컴파일러에게 유용한 정보를 제공한다. 그리고 새로운 애너테이션을 정의할 때 사용하는 메타 애너테이션을 제공한다.
| 애너테이션 | 설명 |
|---|---|
| @Override | 컴파일러에게 오버라이딩하는 메서드라는 것을 알린다. |
| @Deprecated | 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다. |
| @SuppressWarnings | 컴파일러의 특정 경고메세지가 나타나지 않게 해준다. |
| @SafeVarargs | 제네릭 타입의 가변인자에 사용한다. (JDK1.7) |
| @FunctionalInterface | 함수형 인터페이스라는 것을 알린다. (JDK1.8) |
| @Native | native 메서드에서 참조되는 상수 앞에 붙인다. (JDK1.8) |
| @Target | 애너테이션이 적용가능한 대상을 지정하는데 사용한다. (메타 애너테이션) |
| @Documented | 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다. (메타 애너테이션) |
| @Inherited | 애너테이션이 하위 클래스에 상속되도록 한다. (메타 애너테이션) |
| @Retention | 애너테이션이 유지되는 범위를 지정하는데 사용한다. (메타 애너테이션) |
| @Repeatable | 애너테이션을 반복해서 적용할 수 있게 한다. (JDK1.8) (메타 애너테이션) |
메서드 앞에만 붙일 수 있는 애너테이션으로 상위 메서드를 오버라이딩하는 것이라는 것을 컴파일러에게 알려준다. 이 애너테이션을 붙이면 컴파일러가 같은 이름의 메서드가 상위에 있는지 확인하고 없으면, 에러메세지를 출력한다. 되도록 오버라이딩 할 때 붙이는 것이 좋다.
새로운 버전의 JDK가 소개될 때 새로운 기능이 추가될 뿐만 아니라 기존의 부족했던 기능들을 개선하기도 한다. 이 과정에서 기존의 기능을 대체할 것들이 추가되어도, 이미 여러 곳에서 사용되고 있을지 모르는 기존의 것들을 삭제할 수 없다. 그래서 더 이상 사용되지 않는 필드나 메서드에 @Deprecated를 붙이는 것이다. 이 애너테이션이 붙은 대상은 다른 것으로 대체되었으니 더 이상 사용하지 않을 것을 권한다는 의미다.
함수형 인터페이스를 선언할 때, 이 애너테이션을 붙이면 컴파일러가 함수형 인터페이스를 올바르게 선언했는지 확인하고 잘못된 경우 에러를 발생시킨다. 여기서 함수형 인터페이스는 추상 메서드가 하나뿐이어야 한다는 제약이 있다.
컴파일러가 보여주는 경고메세지가 나타나지 않게 억제한다. 경우에 따라 경고가 발생할 것을 알면서도 묵인해야 할 때가 있는데 이 때 사용하면 유용하다. 또한 억제할 수 있는 경고메세지가 여러가지 있는데 주로 사용하는 것은 "deprecation", "unchecked", "rawtypes", "varargs" 정도이다. "deprecation"은 @Deprecated가 붙은 대상을 사용해서 발생하는 경고를, "unchecked"는 제네릭 타입을 지정하지 않았을 때 발생하는 경고를, "rawtypes"는 제네릭을 사용하지 않아서 발생하는 경고를, "varargs"는 가변인자의 타입이 제네릭 타입일 때 발생하는 경고를 억제한다.
만약 2개 이상의 경고를 작성하려면 배열형태처럼 작성하면 된다.
단, 이 애너테이션을 너무 남발하면 추후 개발된 코드의 경고를 억제할 수 있으니 해당 대상에만 애너테이션을 붙여서 경고의 억제범위를 최소화하는 것이 좋다.
메서드에 선언된 가변인자 타입이 non-reifiable(제네릭)타입일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 "unchecked"경고가 발생한다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 @SafeVarargs를 사용해야 한다.
이 애너테이션은 static이나 final이 붙은 메서드와 생성자에만 붙일 수 있다. 즉, 오버라이드될 수 있는 메서드에는 사용할 수 없다는 뜻이다.
또한 @SafeVarargs로 "unchecked"경고는 억제가능하지만 "varargs"경고는 억제 할 수 없기 때문에 @SuppressWarnings("varargs")와 같이 사용해야한다.
메타 애너테이션은 애너테이션에 붙이는 애너테이션으로 애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간등을 지정하는데 사용된다.
애너테이션이 적용가능한 대상을 지정하는데 사용된다. 여러개의 값을 지정할 때는 배열에서처럼 중괄호를 사용하면 된다.
@Target으로 지정할 수 있는 애너테이션 적용대상의 종류는 아래와 같다.
| 대상 타입 | 의미 |
|---|---|
| ANNOTATION_TYPE | 애너테이션 |
| CONSTRUCTOR | 생성자 |
| FIELD | 필드 (멤버변수, enum 상수) |
| LOCAL_VARIABLE | 지역변수 |
| METHOD | 메서드 |
| PACKAGE | 패키지 |
| PARAMETER | 매개변수 |
| TYPE | 타입 (클래스, 인터페이스, enum) |
| TYPE_PARAMETER | 타입 매개변수 (JDK1.8) |
| TYPE_USE | 타입이 사용되는 모든 곳 (JDK1.8) |
⚠️ 주의
FIELD는 기본형에 TYPE_USE는 참조형에 사용된다는 점을 주의하자.
애너테이션이 유지되는 기간을 지정하는데 사용된다.
| 유지 정책 | 의미 |
|---|---|
| SOURCE | 소스 파일에만 존재, 클래스파일에는 존재하지 않음 |
| CLASS | 클래스 파일에 존재, 실행시에 사용불가, 기본값 |
| RUNTIME | 클래스 파일에 존재, 실행시에 사용가능 |
유지정책을 RUNTIME으로 하면 리플렉션을 통해 클래스 파일에 저장된 애너테이션의 정보를 읽어서 처리할 수 있다. 유지정책 CLASS는 컴파일러가 애너테이션의 정보를 클래스 파일에 저장할 수 있게는 하지만, 클래스 파일이 JVM에 로딩될 때는 애너테이션의 정보가 무시되어 실행 시에 애너테이션에 대한 정보를 얻을 수 없다.
참고
지역변수에 붙은 애너테이션은 컴파일러만 인식할 수 있으므로, 유지정책이 RUNTIME인 애너테이션을 지역변수에 붙여도 잘 인식이 안 될때가 있다.
애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다.
애너테이션이 하위 클래스에 상속되도록 한다. @Inherited가 붙은 애너테이션을 상위 클래스에 붙이면 하위 클래스에 붙이면 하위 클래스도 이 애너테이션이 붙은것과 같이 인식된다.
보통은 하나의 대상에 한 종류의 애너테이션을 붙이는데 여러번 붙일 경우 이 애너테이션을 사용한다.
일반적인 애너테이션과 달리 같은 이름의 애너테이션이 여러 개가 하나의 대상에 적용될 수 있기 때문에 이 애너테이션들을 하나로 묶어서 다룰 수 있는 애너테이션도 추가로 정의해야 한다.
@interface Todos {
Todo[] value();
}
@Repeatable(Todos.class)
@interface Todo {
String value();
}
네이티브 메서드에 의해 참조되는 상수 필드에 붙이는 애너테이션이다.
네이티브 메서드는 JVM이 설치된 OS의 메서드를 말한다. 네이티브 메서드는 보통 C언어로 작성되어 있는데, 자바에서는 메서드의 선언부만 정의하고 구현은 하지 않는다. 그래서 추상 메서드처럼 선언부만 있고 몸통이 없다.
모든 클래스의 조상인 Object 클래스의 메서드들은 대부분 네이티브 메서드이다. 네이티브 메서드는 자바로 정의되어 있기 때문에 호출하는 방법은 자바의 일반 메서드와 다르지 않지만 실제로 호출되는 것은 OS의 메서드이다.
네이티브 메서드를 호출 할때 자바에 정의된 네이티브 메서드와 OS의 메서드를 연결해주는 작업이 추가로 필요한데 이 역할을 JNI가 한다.
우리는 커스텀한 애너테이션을 정의할 수 있다. 방법은 @interface 키워드를 사용하고 내용부는 인터페이스와 동일하다.
@interface 애너테이션이름 {
타입 요소이름();
}
애너테이션 내에 선언된 메서드를 애너테이션의 요소라고 한다. 애너테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드 형태를 가지며, 상속을 통해 구현하지 않아도 된다. 다만, 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 적용해주어야 한다. 순서는 상관없다. 또한 애너테이션의 요소는 기본값을 가지며 요소를 작성하지 않으면 기본값이 자동 적용된다.
또한, 애너테이션 요소가 오직 하나뿐이고 이름이 value인 경우 이름이 생략 가능하다.
요소의 타입이 배열인 경우, 괄호 {}를 사용해서 여러 개의 값을 지정할 수 있다.
기본 값을 지정할 때도 괄호 {}를 사용할 수 있다.
모든 애너테이션의 조상은 Annotation이다. 그러나 애너테이션은 상속이 허용되지 않으므로 명시적으로 Annotation을 상위 타입으로 지정할 수 없다. 또한 Annotation은 애너테이션이 아니라 인터페이스이다.
값을 지정할 필요가 없는 경우 애너테이션의 요소를 하나도 정의하지 않을 수 있다. 이 애너테이션을 마커 애너테이션이라고 한다.
- 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용한다.
- ()안에 매개변수를 선언할 수 없다.
- 예외를 선언할 수 없다.
- 요소를 타입 매개변수로 정의할 수 없다.