본문 바로가기

프로그래밍언어/JAVA

[JAVA] Annotation

반응형

1. annotation

애너테이션은 주석, 주해, 메모 라는 뜻을 가지고 있다. 자바에서 에너테이션은 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이다. '@' 기호를 붙여서 표현하며, 주석처럼 프로그램에 직접적인 영향을 미치지 않으면서 정보를 제공한다.

대표적인 애너테이션으로 @Override, @Test 등을 사용하곤 하는데, 각각 이후에 나오는 소스코드가 오버라이딩을 위한 메서드, 테스트를 위한 메서드임을 알려주는 역할을 한다.

이와같이 JDK 에서 미리 제공하는 애너테이션들을 표준 애너테이션이라고 하는데, 주로 컴파일러를 위한 것들이다. 이외에도 개발자가 직접 애너테이션을 정의할 수 있는데, 이때 메타 에너테이션을 사용하면된다.


2. 표준 애너테이션

자바에서 제공하는 기본 표준 에너테이션들은 몇 개 없다. 이중에 일부는 메타 애너테이션으로 새로운 애너테이션을 정의하는데 사용된다.

- @Override
메서드 앞에만 붙일 수 있는 애너테이션으로 해당 메서드가 조상의 메서드를 오버라이딩 하는 것이라는걸 컴파일러에게 알려주는 역할을 한다.

만약 애너테이션이 없이 오버라이딩을 하는 경우에는 메서드의 이름에 오타가 나거나 잘못된 메서드를 오버라이딩 하여도 컴파일러는 이를 오버라이딩한 메서드가 아닌 새로운 메서드로 인식하고 실행한다. 하지만 Override 애너테이션을 추가하면 컴파일러가 같은 이름의 메서드가 조상에 있는지 확인하고 없는 경우 에러메시지를 출력한다.

- @Deprecated
특정한 기능이 삭제되면서 더이상 사용하지 않는 필드나 메서드가 발생한다. 이러한 곳에 붙이는 애너테이션이 @Deprecated 이다. 이 애너테이션은 해당 대상이 다른 대상으로 대체되었으니 더 이상 사용하지 않는 것을 권한다는 의미이다.

해당 애너테이션이 붙은 대상을 사용하여도 @Override 와 같이 에러메시지가 출력되지는 않는다. 대신 더 이상 사용하지 않는 대상이라는 의미의 알림 메시지가 출력된다.

- @FunctionalInterface
함수형 인터페이스를 선언할 때 사용한다. 이 에너테이션을 붙이면 컴파일러가 함수형 인터페이스를 올바르게 선언했는지 확인하고, 잘못된 경우 에러 메시지를 발생시킨다. 함수형 인터페이스는 추상 메서드가 하나만 있어야 한다는 제약이 있다.

- @SuppressWarnings
컴파일러가 표출하는 경고메시지를 나타나지 않게 해준다. 이전의 애너테이션과 같이 경고 메시지를 표출하는 애너테이션들이 있는데, 이 애너테이션을 사용하면 이런 경고 메시지들이 출력되지 않게 한다.

@SuppressWarnings 애너테이션은 묵인하고자 하는 경고의 타입을 정할 수 있다. 주로 "deprecation", "unchecked", "rawtypes", "varargs" 등과 같은 타입들이 있다. 이들은 다음과 같이 코드에 정의하여 사용할 수 있다.

 

@SuppressWarnings("deprecation") // depreaction 경고 메시지 무시
@SuppressWarnings({"deprecation", "unchecked"}) // deprecation 과 unchecked 에 해당하는 메시지 무시

 

컴파일 시에 '-Xlint' 옵션을 주면 경고 메시지에 대한 자세한 내용을 알 수 있다. 이 내용 중에는 메시지의 타입에 대한 내용도 포함하는데, 이러한 내용을 확인하여 해당 메시지를 무시하고자 하는 경우에 사용할 수 있다.

- @SafeVarargs
메서드에 선언된 가변인자의 타입이 non-reifiable 타입인 경우에 해당 메서드를 선언하는 부분과 호출하는 부분에서 "unchecked" 경고가 발생한다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 해당 애너테이션을 사용할 수 있다.

reifiable 타입은 컴파일 후에도 제거되지 않는 타입을 의미한다. 지네릭스와 같이 컴파일 후에 타입이 제거되는 경우는 non-reifiable 타입이라고 한다.

이 애너테이션은 생성자와 static, final 이 붙은 메서드에만 붙일 수 있다. 즉 오버라이드 될 수 있는 메서드에는 사용할 수 없다.

이 애너테이션을 사용하면 메서드를 호출하는 곳에서 발생하는 경고도 무시된다. @SuppressWarnings 를 사용하는 경우에는 메서드 선언하는 곳의 경고만 무시하기 때문에 호출하는 곳에도 일일이 애너테이션을 달아줘야 하지만, @SafeVarargs 는 선언하는 곳에만 달아주어도 호출시의 경고를 억제한다. 이때문에 @SuppressWarnings("varargs") 와 @SafeVarargs 를 같이 사용하는 경우가 많다.

 


3. 메타 애너테이션

메타 애너테이션은 '애너테이션을 위한 애너테이션' 이다. 애너테이션에 붙이는 애너테이션으로 새로운 애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간 등을 지정하는데 사용된다.

- @Target
애너테이션이 적용가능한 대상을 지정하는데 사용된다. 아래와 같이 중괄호 안에 여러 개의 값을 위치시켜 여러 개의 값을 지정할 수 있다.

 

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
	String[] value();
}


@Target 으로 지정할 수 있는 적용대상의 종류는 다음과 같다.

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


- @Retention
애너테이션이 유지되는 기간을 지정하는데 사용한다. 애너테이션의 유지 정책은 다음과 같다.

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


@Override 나 @SuppressWarnings 처럼 컴파일러에 의해 사용되는 애너테이션은 SOURCE 정책으로 선언되어 있다.

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

유지 정책 CLASS 는 컴파일러가 애너테이션의 정보를 클래스 파일에 저장할 수 있게는 하지만 클래스 파일이 JVM 에 로딩될 때는 애너테이션의 정보가 무시되어 실행 시에 애너테이션에 대항 정보를 얻을 수 없다.

지역변수에 붙은 애너테이션은 컴파일러만 인식할 수 있기 때문에 유지 정책이 RUNTIME 인 애너테이션을 지역변수에 붙여도 실행 시에는 인식되지 않는다.

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

- @Inherited
애너테이션이 자손 클래스에 상속되도록 한다. @Inherited 애너테이션이 붙은 클래스를 조상 클래스로 둔 자손 클래스들은 이 조상 클래스에 붙은 다른 애너테이션도 상속받게된다.

- @Repeatable
보통은 하나의 대상에 한 종류의 애너테이션만 붙일 수 있는데, @Repeatable 이 붙은 애너테이션은 여러 번 붙일 수 있다.

- @Native

네이티브 메서드에 의해 참조되는 상수 필드에 붙이는 애너테이션이다.
네이티브 메서드는 JVM 이 설치된 OS 의 메서드를 의미한다. 네이티브 메서드는 보통 C 언어로 작성되어 있는데, 자바에서는 메서드의 선언부만 정의하고 구현은 하지 않는다. 그래서 추상 메서드와 같이 선언부만 존재한다.

자바의 모든 클래스의 조상인 Object 클래스의 메서드들은 대부분 네이티브 메서드이다. 네이티브 메서드는 자바로 정의되어 있기에 일반 메서드와 같이 호출할 수 있지만, 구현부가 따로 작성되어있지 않다 실제로 호출되는 내용은 OS 의 메서드이다.

네이티브 메서드들은 선언 후에 따로 JNI, Java Native Interface 를 통해 네이티브 메서드와 OS 메서드들을 연결하는 작업이 필요하다.


4. 애너테이션 타입 정의하기

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

 

@interface AnnotationName {
    type element(); // 애너테이션의 요소를 선언한다.
    ...
}


- 애너테이션의 요소

애너테이션 내에 선언된 메서드를 '애너테이션의 요소, element' 라고 한다. 아래에 선언된 애너테이션은 다섯 개의 요소를 가진다.

 

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

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


애너테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며, 상속을 통해 구현하지 않아도 된다. 다만, 애너테이션을 적용힐 때 이 요소들의 값을 빠짐없이 지정해주어야 한다. 요소의 이름도 같이 적어서 할당하기 때문에 순서는 상관없다. count 와 같이 default 키워드를 사용해 기본값을 정해줄 수 있다.

 

@TestInfo(
    count=3, testedBy="jamie",
    testTools={"Junit", "AutoTester"},
    testType=TestType.FIRST,
    testDate=@DateTime(yymmdd="220222", hhmmss="214900")
)
public class AnnotatedClass { ... }


만약 요소의 이름이 value 이고 요소가 하나밖에 없으면 요소의 이름을 생략하고 값만 적어서 할당할 수 있다.

- java.lang.annotation.Annotation

모든 애너테이션의 조상은 Annotation 이다. 그러나 애너테이션은 상속이 허용되지 않으므로 아래와 같이 명시적으로 Annotation 을 상속받도록 지정할 수 없다.

Annotation 은 인터페이스로 정의되어 있다. equals(), hashCode(), toString() 메서드가 정의되어 있기 때문에 모든 애너테이션 객체에서는 해당 메서드들을 호출할 수 있다.

- 마커 애너테이션, Marker Annotation

값을 지정할 필요가 없는 경우, 애너테이션의 요소를 하나도 정의하지 않을 수 없다. Serializable 이나 Cloneable 인터페이스처럼 요소가 하나도 정의되지 않은 애너테이션을 마커 애너테이션이라고 한다.

- 애너테이션 요소의 규칙

애너테이션의 요소를 선언할 때는 다음의 규칙을 지켜야 한다.

 

1) 요소의 타입은 기본형, String, enum, 애너테이션, Class 만 허용된다.
2) () 안에 매개변수를 선언할 수 없다.
3) 예외를 선언할 수 없다.
4) 요소를 타입 매개변수로 정의할 수 없다.
반응형

'프로그래밍언어 > JAVA' 카테고리의 다른 글

[JAVA] 쓰레드 구현 및 실행 (Thread, Runnable, start(), run())  (0) 2022.03.10
[JAVA] 프로세스와 쓰레드  (1) 2022.03.08
[JAVA] Enums  (0) 2022.02.21
[JAVA] Generics  (0) 2022.02.16
[JAVA] Collections  (0) 2022.02.04