본 게시글은 백기선 님의 live-study 과제를 수행하면서 작성한 글입니다.
목표
자바의 애노테이션에 대해 학습하세요.
학습할 것 (필수)
- 애노테이션 정의하는 방법
- @retention
- @target
- @documented
- 애노테이션 프로세서
애노테이션이란?
소스 코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것.
애노테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서 다른 프로그램에게 유용한 정보를 제공할 수 있다.
예를 들어 우리는 테스트 메서드를 작성할 때 메서드 위에 @Test 애노테이션을 붙인다.
@Test
void testMethod() {
//...
}
여기서 @Test 애노테이션은 '이 메서드를 테스트해야 한다.'라는 것을 JUnit과 같은 테스트 프레임워크에게 알리는 역할을 할 뿐, 메서드가 포함된 프로그램 자체에는 주석과 같이 아무런 영향을 끼치지 않는다.
자바에서 제공하는 표준 애노테이션
- JDK에서 제공하는 표준 애노테이션은 'java.lang.annotaion' 패키지에 포함되어 있다.
@Override
- 위치 : 메서드
- 역할 : 조상 메서드를 오버라이딩하는 메서드라는 것을 컴파일러에게 알려준다.
class Parent {
void parentMethod() {}
}
class Child extends Parent {
void parentMethodd() { ... }
}
Child 클래스에선 조상 클래스인 Parent 클래스의 parentMethod()를 오버라이딩을 하려 했지만, 메서드명 끝에 d를 하나 더 붙여서 오타가 발생했다. 이 경우 컴파일러는 parentMethodd()를 오버라이딩된 메서드가 아닌 새로운 메서드라고 판단한다.
class Child extends Parent {
@Override
void parentMethodd() { ... }
}
하지만 다음과 같이 @Override 애노테이션을 사용하면 컴파일러는 같은 이름의 조상 메서드가 있는지 확인하고, 만약 없다면 아래와 같은 에러 메시지를 출력해준다.
java: method does not override or implement a method from a supertype
@Deprecated
- 위치 : 필드, 메서드
- 역할 : 이 애노테이션이 붙은 대상은 다른 것으로 대체되었으니 더 이상 사용하지 않는 것이 좋다고 알려주는 것.
새로운 버전의 JDK가 나올 때, 새로운 기능이 추가되거나 기존의 부족한 부분들을 개선하기도 한다. 이 과정에서 기존에 있던 것들을 대체하는 기능이 나오더라도 호환성을 위해 함부로 삭제 할 수 없다.
그래서 @Deprecated 애노테이션을 통해 다른 것으로 대체되었으니 더 이상 사용하지 않는 것이 좋다고 알려준다.
실제로 @Deprecated 애노테이션이 붙어있는 java.util.getDate() 메서드를 예로 살펴보면 메서드 설명에 Calendar.get() 메서드로 대체되었다고 설명하고 있다.
@FunctionalInterface
- 위치 : 함수형 인터페이스
- 역할 : 컴파일러가 함수형 인터페이스를 올바르게 선언했는지 확인하고, 잘못 선언된 경우 에러를 발생시킨다.
@FunctionalInterface
public interface FunctionalInterfaceSample {
void hello();
}
@SuppressWarnings
- 역할 : 컴파일러가 보여주는 경고 메시지가 출력되지 않게 해 준다.
경우에 따라 컴파일러가 발생하는 경고를 무시해야 할 때가 있는데, 이 경우에 경고 메시지가 출력되지 않게 해 준다.
@SuppressWarnings 애노테이션이 막아주는 경고 메시지의 종류는 여러 가지가 있는데, 대표적으로 다음과 같다.
- deprecation : @Deprecated가 붙은 대상의 경고를 무시.
- unchecked : 지네릭스로 타입을 지정하지 않을 때 발생하는 경고를 무시.
- rawtypes : 지네릭스를 사용하지 않아서 발생하는 경고를 무시.
- varargs : 가변 인자의 타입이 지네릭 타입일 때 발생하는 경고를 무시.
//지네릭스 타입을 지정하지 않아 경고 발생
List list = new ArrayList();
자바 컴파일 시에 -Xlint 옵션을 사용하면 경고 메시지를 출력할 수 있다.
App.java:14: warning: [rawtypes] found raw type: ArrayList
ArrayList list = new ArrayList();
출력된 경고 메시지 중 []에 있는 것이 경고 메시지의 종류이다.
@SuppressWarnings("rawtypes")
List list = new ArrayList();
위와 같이 @SuppressWarnings()의 인자로 경고 메시지 종류를 문자열로 전달하고 다시 컴파일해보면 경고 메시지는 출력되지 않는다.
여러 경고 메시지를 동시에 사용하려면 다음과 같이 문자열 배열을 인자로 전달하면 된다.
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
@SafeVarargs
위치 : static 또는 final이 붙은 메서드나 생성자
역할 : 메서드에 선언된 가변 인자의 타입이 non-refiable타입일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 "unchecked" 경고가 발생하는데 이 경고를 무시하기 위해 사용한다.
non-refiable 타입 : 지네릭스와 같이 컴파일 이후에 제거되는 타입.
class Hello<T> {
T[] arr;
@SafeVarargs
public Hello(T... arr) {
this.arr = arr;
}
@SafeVarargs
public static <T> Hello<T> asList(T... a) {
return new Hello<>(a);
}
}
메타 애노테이션
메타 애노테이션은 애노테이션에 붙이는 애노테이션으로 애노테이션을 정의할 때 애노테이션의 적용 대상이나 유지기간 등을 지정하는 데 사용된다.
@Target
애노테이션이 적용 가능한 대상을 지정하는 데 사용되며 적용 대상의 종류는 다음과 같다.
- ANNOTATIN_TYPE : 애노테이션
- CONSTRUCTOR : 생성자
- FIELD : 필드(멤버 변수, enum 상수), 기본형에 사용
- LOCAL_VARIABLE : 지역변수
- METHOD : 메서드
- PACKAGE : 패키지
- PARAMETER : 매개변수
- TYPE : 타입(클래스, 인터페이스(애노테이션 포함), enum)을 선언할 때
- TYPE_PARAMETER : 지네릭의 타입 매개변수(JDK 1.8) (예: class Hello<@MyAnnotation T> { })
- TYPE_USE : 타입이 사용되는 모든 곳(JDK 1.8), 타입의 변수를 선언할 때
원하는 적용 대상을 @Target 애노테이션의 인자로 전달하면 된다.
import static java.lang.annotation.ElementType.*;
@Target({FIELD, TYPE, METHOD})
@interface MyAnnotation { }
@MyAnnotation //TYPE
class Hello {
@MyAnnotation //FIELD
int num;
@MyAnnotation //METHOD
public int getNum() {
return num;
}
}
@ Retention
애노테이션이 유지되는 기간을 지정하는 데 사용된다. 애노테이션의 유지 정책의 종류는 다음과 같다.
- SOURCE : 소스 파일에만 존재, 클래스 파일에는 존재하지 않음.
- CLASS(디폴트 값) : 클래스 파일에 존재, 실행 시에 사용불가.
- RUNTIME : 클래스 파일에 존재, 실행시에 사용 가능.
SOURCE
@Override처럼 컴파일러가 사용하는 애노테이션의 유지 정책이기 때문에 컴파일러를 작성할 것이 아니라면 이 유지 정책은 사용할 일이 없다.
RUNTIME
실행 시에 리플렉션을 통해 클래스 파일에 저장된 애노테이션의 정보를 읽어서 처리할 수 있다.
CLASS
클래스 파일에 JVM에 로딩될 때는 애노테이션이 무시되어 실행 시에 정보를 얻을 수 없기 때문에 디폴트 값이지만 잘 사용되지 않는다.
@ Documented
애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다.
@Inherited
애노테이션이 자식 클래스에 상속되도록 한다.
부모 클래스에 붙은 애노테이션에 @Inherited가 있다면 자식 클래스에도 해당 애노테이션이 붙어 있는 것과 같다.
import static java.lang.annotation.ElementType.*;
@Inherited
@Target(TYPE)
@interface MyAnnotation { }
@MyAnnotation
class Parent { }
//@MyAnnotation이 붙어있는 것으로 인식
class Child extends Parent { }
@Repeatable
하나의 대상에 같은 애노테이션을 여러 번 붙일 수 있도록 해준다.
일반 애노테이션과 달리 하나의 대상에 같은 이름의 애노테이션이 여러 개가 사용되기 때문에 이를 묶어서 보관할 컨테이너 애노테이션을 별도로 사용해야 한다.
//여러 개의 MyAnnotation을 담을 컨테이너 애노테이션
@interface MyAnnotations {
MyAnnotation[] value(); //이름이 반드시 value여야함
}
@Repeatable(MyAnnotations.class) //인자로 컨테이너 애노테이션 지정
@interface MyAnnotation {
String value();
}
@MyAnnotation("Hello")
@MyAnnotation("World")
@MyAnnotation("!!")
class Hello {
}
@Native
네이트브 메서드에 의해 참조되는 상수 필드에 붙이는 애노테이션이다.
애노테이션 정의하는 방법
@interface 애노테이션이름 {
타입 요소이름();
...
}
애노테이션에 선언된 메서드를 요소라고 하며, 반환 값이 있고 매개변수는 없는 추상 메서드의 형태를 가진다.
애노테이션을 사용할 때 기본값이 있는 요소들을 제외한 나머지 요소들은 무조건 값을 전달해줘야 한다.
- 요소의 타입은 기본형, String, enum, 애노테이션, Class만 허용된다.
- () 안에 매개변수를 선언할 수 없다.
- 예외를 선언할 수 없다.
- 요소를 타입 매개변수로 정의할 수 없다.
값을 지정하지 않았을 때 사용될 기본값을 지정할 수 있다.
@Target(TYPE)
@interface MyAnnotation {
int count() default 1; //기본값 지정 1
}
@MyAnnotation(count = 10)
@MyAnnotation() //생략시 기본값인 1
요소의 타입이 배열인 경우, {} 사용하여 여러 개의 값을 지정할 수 있고, 값이 하나인 경우 {}를 생략할 수 있다.
@Target(TYPE)
@interface MyAnnotation {
String[] strings();
}
@MyAnnotation(strings = {"a", "b", "c"})
@MyAnnotation(strings = "a") //값이 1개 인경우 {} 생략 가능.
@MyAnnotation(strings = {}) //값이 0개인 경우에 {} 생략 불가능.
요소의 개수가 1개이고, 이름이 value인 경우, 요소의 이름을 생략할 수 있다.(배열인 경우에도 사용 가능)
@interface MyAnnotation {
String value();
}
@MyAnnotation("abcd")
값을 지정할 필요가 없다면, 요소를 하나도 정의하지 않아도 되는데 이를 '마커 애노테이션'이라고 한다.
@Target(TYPE)
@interface MyMarkerAnnotation { }
@MyMarkerAnnotation
class Hello {
}
애노테이션 프로세서
- javac에 있는 빌드 툴로, 컴파일 시점에 애노테이션에 대해 다양한 처리를 위해 사용된다.
- 컴파일 시점에 컴파일러가 소스 코드의 애노테이션 정보를 읽어, 새로운 코드를 생성하거나 변경할 수 있고, 리소스 파일의 생성도 가능하다.
애노테이션 프로세서의 예
- lombok
- @Override
- AutoService : java.utilServiceLoader용 파일 생성 유틸리티
- Dagger2 : 컴파일 타임 DI 제공
장점
- 컴파일 시점에 조작하기 때문에 런타임 시 비용이 제로이다.
단점
- 기존의 코드를 고치는 방법은 공개된 API를 제공하지 않는다.
출처
- 자바의 정석 3판(남궁성 저)
- https://ncucu.me/157
'자바 > 스터디' 카테고리의 다른 글
[자바 스터디] 14주차 과제 - 제네릭(Generic) (0) | 2021.02.26 |
---|---|
[자바 스터디] 13주차 과제 - I/O (0) | 2021.02.19 |
[ 자바 스터디] 11주차 과제 - 열거형(Enum) (0) | 2021.01.29 |
[자바 스터디] 10주차 과제 - 쓰레드 (0) | 2021.01.23 |
[자바 스터디] 9주차 과제 - 예외 처리 (0) | 2021.01.15 |
댓글