목표
자바의 애노테이션에 대해 학습하세요.
학습할 것 (필수)
- 애노테이션 정의하는 방법
- @retention
- @target
- @documented
- 애노테이션 프로세서
개념 정리
- 메타 데이터(metadata) : 데이터에 대한 데이터. 즉, 다른 데이터를 설명해주는 데이터입니다.
- 예를들어, 카메라로 사진을 찍었을 때 사진에 대한 [촬영한 시간, 장소, 카메라 모델명, 플래쉬 사용 유무, 등등]이 메타데이터라고 할 수 있습니다.
- 애노테이션(annotation) : 사전적 의미로 주석을 의미하며 프로그램에 대한 데이터를 제공하는 메타데이터의 한 형태 입니다.
- 애노테이션의 용도
- 컴파일러에 제공하는 정보 -> 컴파일러는 애노테이션을 사용하여 에러를 체크하거나 에러메시지를 억제할 수 있습니다. ex) @Override, @SuppressWarnings
- 컴파일 시간 및 배포 시간 처리 -> 소프트웨어 개발툴이 애노테이션 정보를 처리하여 코드, XML 파일등을 생성할 수 있습니다. (애노테이션을 사용한 곳에 특정 코드를 추가할 수 있습니다. ex) Lombok의 @Getter, @Setter)
- 런타임 처리 -> 일부 애너테이션은 런타임에 특정 기능을 실행하도록 정보를 제공합니다. (Java Reflection)
- 자바 리플렉션(Java Reflection) : 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API 입니다. 런타임 시에 클래스 이름만 알고있다면 클래스에 대한 정보를 가져오고 활용할 수 있게 해줍니다.
애노테이션 정의하는 방법
아래는 애노테이션을 직접 정의하는 방법 입니다.
public @interface MyAnnotation {
}
'@interface'는 애노테이션 타입(annotation type)을 선언하는 키워드 입니다. 애노테이션 타입 선언을 일반적인 인터페이스(interface) 선언과 구분하기 위해 interface 앞에 기호 @를 붙입니다.
기호 @는 interface와 별개의 토큰입니다. 따라서 @와 interface를 띄워써도 오류는 없지만 스타일상 같이 붙여서 쓰는걸 권장합니다.
애노테이션 필드
애노테이션에 필드같은 요소들을 정의할 수 있습니다.(사실은 추상 메서드)
- 요소의 타입은 기본형, String, enum, 애노테이션, Class만 가질 수 있습니다.
- ()안에 매개변수를 선언할 수 없습니다.
- 예외를 선언할 수 없습니다.
- 요소를 타입 매개변수로 정의할 수 없습니다.
- 배열을 선언할 수 있습니다. String[] arr();
- default값을 지정할 수 있습니다. int number() default 500;
요소는 아래와 같이 애노테이션을 적용하고 각각의 요소에 값을 주고 사용할 수 있습니다.
아래 코드는 자바 리플렉션을 사용해서 MyClass 의 method()에 적용된 MyAnnotation의 요소값들을 가져오는 실행 코드입니다. 간단하게 순서를 정리하자면 다음과 같습니다.
- 1. MyClass.class.getMethods() 를 사용해서 MyClass에 정의된 모든 메서드들을 Mythod[] 배열에 넣습니다.
- 2. 각각의 메서드에 isAnnotationPresent(MyAnnotation.class) 를 사용해서 MyAnnotation이 붙어있는 메서드인지 확인합니다.
- MyAnnotation이 붙어있는 메서드라면 getDeclaredAnnotation(MyAnnotation.class)를 사용해서 어노테이션을 변수에 초기화 시킵니다.
- myAnnotation 변수를 사용해서 각각의 요소를 출력합니다.
자바 리플렉션에 대해서는 좀 더 아래쪽에서 가볍게 정리만 하고 넘어가겠습니다.
애노테이션 바이트 코드
아래는 MyAnnotation.java 파일과 컴파일하고 바이트코드를 열어본것입니다.
- MyAnnotation.java 파일
public @interface MyAnnotation {
String value();
}
- MyAnnotation 의 바이트코드
// class version 52.0 (52)
// access flags 0x2601
public abstract @interface study12th/MyAnnotation implements java/lang/annotation/Annotation {
// compiled from: MyAnnotation.java
// access flags 0x401
public abstract value()Ljava/lang/String;
}
바이트코드를 보면 abstract와 implements java/lang/annotation/Annotation 이 추가된걸 확인할 수 있습니다.
- abstract는 애노테이션이 인터페이스의 특징을 가지고 있다는것을 의미합니다. 즉, 애노테이션도 인터페이스의 한 종류입니다.
- java.lang.annotation.Annotation 을 상속받고 있습니다.
- 코드는 implements 이지만 인터페이스 끼리 상속받은 것이기 때문에 실제로는 'extends java.lang.annotation.Annotation' 과 같습니다.
- 모든 애노테이션(@interface)은 java.lang.annotation.Annotation 을 상속받습니다.
애노테이션 타입 선언(@interface)은 문맥 자유 구문(Context-free grammer,CFG)으로부터 다음과 같은 제한을 가집니다.
- 애노테이션 요소의 타입 선언은 제네릭일 수 없습니다.
- extends 절을 가질 수 없습니다.(애노테이션 타입은 암묵적으로 java.lang.annotation.Annotation을 상속합니다.)
- 메서드는 매개변수(Parameter)를 가질 수 없습니다.
- 메서드는 타입 매개변수(Type Parameter)를 가질 수 없습니다.
- 메서드 선언은 throws 절을 가질 수 없습니다.
하지말란것은 역시 다 해봐야 미련없이 떠날 수 있습니다.
자바 8에서 추가된 애노테이션 사용처
위의 문서를 보면 자바 8에서는 다음과 같은곳에도 애노테이션을 사용할 수 있습니다.
- 클래스 인스턴스를 생성할 때
- new @Interned MyObject();
- 타입 캐스트
- myString = (@NonNull String) str;
- 인터페이스 구현(implements)
- class UnmodifiableList<T> implements @Readonly List<@Readonly T> { . . . }
- 예외를 throws 할 때
- void monitorTemperature() throws @Critical TemperatureException { . . . }
자바 리플렉션 (Java Reflection)
모든 클래스 파일은 클래스로더(Classloader)에 의해 메모리에 올라갈 때, 클래스에 대한 정보가 담긴 객체를 생성하는데 이 객체를 클래스 객체라고 합니다. 이 객체를 참조할 때는 '클래스이름.class'의 형식을 사용합니다.
클래스이름.class 를 이용해서 클래스의 필드, 생성자, 메서드에 대한 정보를 얻을 수 있습니다.
메서드명 | 리턴타입 | 설명 |
getFields() | Field[] | 접근 제어자가 public인 필드들을 Field 배열로 반환 부모 클래스의 필드들도 함께 반환합니다. |
getConstructors() | Constructor[] | 접근 제어자가 public인 생성자들을 Constructor 배열로 반환. 부모 클래스의 생성자들도 함께 반환합니다. |
getMethods() | Method[] | 접근 제어자가 public인 메서드들을 Method 배열로 반환. 부모 클래스의 메서드들도 함께 반환합니다. |
getDeclaredFields() | Field[] | 접근 제어자에 상관없이 모든 필드들을 Field배열로 반환. 부모 클래스의 필드들은 반환하지 않습니다. |
getDeclaredConstructors() | Constructor[] | 접근 제어자에 상관없이 모든 생성자들을 Constructor배열로 반환. 부모 클래스의 생성자들은 반환하지 않습니다. |
getDeclaredMethod() | Method[] | 접근 제어자에 상관없이 모든 메서드들을 Method배열로 반환. 부모 클래스의 메서드들은 반환하지 않습니다. |
애노테이션 정보를 얻기위한 메서드들
리턴타입 | 메소드명 / 설명 |
boolean | isAnnotationPresent(Class<? Extends Annotation> annotationClass) |
지정한 애노테이션이 적용되었는지 여부를 확인. Class에서 호출했을 경우 상위 클래스에 적용된 경우에도 true를 리턴. |
|
Annotation | getAnnotation(Class<T> annotationClass) |
지정한 애노테이션이 적용되어 있으면 애노테이션을 반환하고 그렇지 않은 경우 null을 반환합니다. Class에서 호출한 경우 상위 클래스에 적용된 애노테이션도 반환합니다. | |
Annotation[] | getAnnotations() |
적용된 모든 애노테이션을 반환합니다. Class에 사용됐을 경우 상위 클래스에 적용된 애노테이션까지 전부 포함해서 반환합니다. 애노테이션이 없을 경우 길이가 0인 배열을 반환합니다. | |
Annotation[] | getDeclaredAnnotation() |
직접 적용된 모든 애노테이션을 리턴합니다. Class에서 호출했을 경우 상위 클래스에 적용된 애노테이션은 포함되지 않습니다.(상위 클래스의 @Inherited가 붙은 애노테이션을 무시합니다.) |
메타 애너테이션
메타 데이터가 데이터에 대한 데이터, 즉 데이터를 위한 데이터인것처럼
메타 애노테이션은 '애노테이션을 위한 애노테이션'. 즉, 애노테이션을 정의하는데 사용하는 애노테이션 입니다.
- 애노테이션의 적용대상(target), 유지기간(retention)등을 지정하는데 사용됩니다.
- 메타 애노테이션은 java.lang.annotation 패키지에 포함되어 있습니다.
애너테이션 | 설명 |
@Target | 애노테이션이 적용가능한 대상을 지정하는데 사용합니다. |
@Documented | 애노테이션 정보가 javadoc으로 작성된 문서에 포함되게 합니다. |
@Inherited | 애노테이션이 자손 클래스에 상속되도록 합니다. |
@Retention | 애노테이션이 유지되는 범위를 지정하는데 사용합니다. |
@Repeatable | 애노테이션을 반복해서 적용할 수 있게 합니다. |
@Retention
(참고 : sas-study.tistory.com/329)
애노테이션이 유지(retention)되는 기간을 지정하는데 사용됩니다. 즉, 어느 시점까지 애노테이션의 메모리를 가져갈지 설정합니다.
애노테이션의 유지 정책(retention policy)의 종류
유지 정책 | 의미 |
SOURCE | 소스 파일에만 존재. 클래스 파일에는 존재하지 않음 |
CLASS | 클래스 파일에 존재. 실행시에 사용 불가. Default 값 |
RUNTIME | 클래스 파일에 존재. 실행시에 사용가능. |
아래는 Retention 애노테이션의 실제 코드입니다.
RetentionPolicy의 값을 넘겨주는 것으로 애노테이션의 유지 정책이 결정된다고 합니다.
그렇다면 참을 수 없으니 RetentionPolicy의 클래스도 실제 코드도 봐야겠죠?
RetentionPolicy는 enum 즉, 열거형입니다.
SOURCE는 애노테이션을 사실상 주석처럼 사용한다고 보면 됩니다 컴파일러가 컴파일 할 때 해당 애노테이션의 메모리는 버립니다.'@Override'나 '@SuppressWarnings' 처럼 컴파일러가 사용하는 애노테이션은 유지 정책이 'SOURCE' 입니다. 컴파일러를 직접 만드는게 아니라면 주석처럼 사용됩니다.
CLASS(디폴트 값)는 컴파일러가 컴파일 시에는 애노테이션의 메모리를 가져가지만, 실질적으로 런타임시에는 사라지게 됩니다. 런타임시에 사라진다는 것은 리플렉션으로 선언된 애노테이션 데이터를 가져올 수 없게 됩니다.
즉, 실행 타임에서 애노테이션에 대한 데이터를 활용할 수 없다는것을 의미합니다. 예를들어 해당 클래스에 있는 애노테이션의 정보를 가져오고 싶을 때 유지정책이 SOURCE나 CLASS 라면 가져오는것이 불가능합니다.
(런타임시에 이미 애노테이션에 대한 메모리가 없기 때문에)
RUNTIME은 애노테이션을 런타임시에까지 사용할 수 있습니다. JVM이 자바 바이트코드가 담긴 class 파일에서 런타임 환경을 구성하고 런타임을 종료할 때까지 메모리는 살아있습니다.
아래부터는 RUNTIME, CLASS, SOURCE 3가지를 각각 사용하며 .class 파일을 열어보겠습니다.
아래는 MyAnnotation 애노테이션입니다. Retention은 RUNTIME으로 되어있지만 계속 바꿔가며 실험해볼것입니다.
다음과 같이 MyClass 클래스의 method() 에 MyAnnotation을 사용했습니다.
먼저 RUNTIME 입니다. [ @Retention(RetentionPolicy.RUNTIME) ]
RUNTIME 유지정책이기 때문에 .class 파일에도 @MyAnnotation가 있습니다.
다음은 CLASS 입니다. [ @Retention(RetentionPolicy.CLASS) ]
CLASS 유지정책 또한 @MyAnnotation이 남아있는것을 확인할 수 있습니다.
RUNTIME과 CLASS의 차이점을 보기위해 바이트코드를 열어보겠습니다.
RetentionPolicy.RUNTIME
RetentionPolicy.CLASS
각각의 코드 3번째 줄 @Lstudy12th/MyAnnotation;() 라인을 보시면 RetentionPolicy.CLASS에는 invisible(보이지 않는) 이라고 써있는게 보입니다. 이말은 즉, CLASS 유지정책은 바이트 코드에서만 확인할 수 있을 정도로만 정의해놓을 뿐 Runtime 환경에서는 역시나 버려지는 메모리라는것입니다.
마지막으로 SOURCE 입니다. [ @Retention(RetentionPolicy.SOURCE) ]
@MyAnnotation이 보이지 않는것을 확인할 수 있습니다. 단지 개발자가 프로그래밍할 때 사용하다가 버려지는 소스이기 때문에 그렇습니다. 대표적인 예로 @Override가 있습니다.
@Target
애노테이션이 적용가능한 대상을 지정하는데 사용합니다. 적용가능한 대상을 여러개 지정하고 싶다면 {} 배열을 사용하면 됩니다.
아래는 Target 애노테이션의 실제 코드 입니다. ElementType의 Enum 요소를 사용해서 애노테이션이 적용 가능한 대상을 결정합니다.
요소 타입이 ElementType[] 배열이기 때문에 적용가능한 대상을 여러 개의 값을 지정할 수 있습니다.
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
'@Target'으로 지정할 수 있는 애노테이션 적용대상의 종류
ElementType | 대상 타입 |
ANNOTATION_TYPE | 애노테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드(멤버변수, enum상수) |
LOCAL_VARIABLE | 지역변수 |
METHOD | 메서드 |
PACKAGE | 패키지 |
PARAMETER | 매개변수 |
TYPE | 타입(클래스, 인터페이스, enum) |
TYPE_PARAMETER | 타입 매개변수 |
TYPE_USE | 타입이 사용되는 곳(타입의 변수를 선언할 때) |
@Documented
애노테이션 정보가 javadoc으로 작성된 문서에 포함되도록 하는 메타 애노테이션 입니다.
자바에서 제공하는 기본 애노테이션 중에 '@Override'와 '@SuppressWarnings'를 제외하고는 모두 @Ducumented 메타 애너테이션이 붙어 있습니다.
아래 캡쳐를 보시면 @Override 와 @SuppressWarnings 에는 @Ducumented가 붙어있지 않습니다.
아래의 @Target, @Native, @Retention, 심지어 @Ducumented 자신까지 @Ducumented가 붙어있습니다(?!)
아래는 클래스의 정보를 담을 수 있는 ClassPreamble 애노테이션 입니다.
해당 애노테이션에 @Ducumented를 붙여서 정의했습니다.
다른 클래스에서 사용한 뒤 javadoc을 생성하면 어떻게 나오는지 한번 보겠습니다.
아래는 @ClassPreamble을 적용한 AnnotationTest 클래스의 코드입니다.
이제 준비가 됐으니 javadoc을 만들어서 확인해봅니다.
(javadoc 만드는법 인텔리제이 기준 shift 두번에 generate javadoc 하셔서 만드시면 됩니다!)
인텔리제이로 javadoc 만드는법
shift 두번 누르고 generate javadoc 을 검색하시고 누르시면 Generate Javadoc 창이 나옵니다.
Output Directory에 저장할 폴더를 선택해주시고
( 저장할 때 폴더를 따로 생성해주지 않고 지정한 폴더에 여러 파일들을 생성합니다!!! 꼭 따로 폴더를 만들고 저장할 폴더로 지정하시길 추천드립니다!!!)
Other Command line arguments 에 -encoding UTF-8 -charset UTF-8 -docencoding UTF-8 까지 지정해줘야
한글이 깨지지않아서 javadoc을 에러없이 성공적으로 만들 수 있습니다.
AnnotationTest 클래스의 문서에 @ClassPreambble의 정보도 함께 포함되어 나오는것을 확인할 수 있습니다.
위에서 Documented 애노테이션에도 @Documented 애노테이션이 사용된 것을 봤으니
ClassPreamble 애너테이션의 javadoc 문서도 봐야겠죠!
@Documented 가 나와있습니다. 즉, @Ducumented 자기 자신도 javadoc 문서에 포함시키는 것입니다.
@Inherited
애노테이션이 자손 클래스에 상속되도록 합니다. '@Inherited'가 붙은 애노테이션을 조상클래스에 붙이면, 자손 클래스도 이 애노테이션이 붙은 것과 같이 인식됩니다.
아래의 코드는 다음과 같습니다.
- @Inherited 애노테이션을 사용한 SomeAnnotation 애노테이션
- @SomeAnnotation을 사용한 Parent 클래스
- Parent클래스를 상속받은 Child 클래스
- Child 클래스의 인스턴스 getAnnotation() 메서드를 사용해서 SomeAnnotation이 있으면 반환받아서 출력합니다.
실행결과로 SomeAnnotation이 출력된것을 확인할 수 있습니다. 따라서 Child 클래스에도 @SomeAnnotation이 적용된것을 확인했습니다.
@Repeatable
보통은 하나의 대상에 한 종류의 애노테이션을 붙이지만 '@Repeatable'이 붙은 애노테이션은 여러 번 붙일 수 있습니다.
(하나의 애노테이션의 배열 요소에 여러개의 요소값을 넣어주는것과 다릅니다.)
@Repeatable(ToDos.class) // @ToDo 애노테이션을 여러 번 반복해서 쓸 수 있게합니다.
@interface ToDo {
String value();
}
일반적인 애노테이션과 달리 같은 이름의 애노테이션이 여러 개가 하나의 대상에 적용될 수 있기 때문에, 이 애노테이션들을 하나로 묶어서 다룰 수 있는 애노테이션도 추가로 정의해야 합니다.
-> 마치 애노테이션들을 담을 배열을 만든다고 생각하면 이해가 쉽습니다.
// @ToDo 애노테이션의 @Repeatable 애노테이션을 위한 ToDos 애노테이션
@interface ToDos {
ToDo[] value();
}
위와 같이 ToDo애노테이션을 담을 컨테이너 애노테이션 ToDos를 만들어야 합니다. 또한 ToDo 애노테이션 배열타입의 요소를 선언해줘야 하고 컨테이너 애노테이션안에 요소의 이름이 반드시 value 이어야 합니다.
표준 애노테이션
자바에서 기본적으로 제공하는 애노테이션들을 자바 표준 애노테이션이라고 합니다. 이안에는 메타 애노테이션도 포함됩니다.
애노테이션 | 설명 |
@Override | 컴파일러에게 오버라이딩하는 메서드라는 것을 알립니다. |
@Deprecated | 앞으로 사용하지 않을 것을 권장하는 대상에 붙입니다. |
@SuppressWarnings | 컴파일러의 특정 경고메시지가 나타나지 않게 해줍니다. |
@SafeVarargs | 지네릭스 타입의 가변인자에 사용합니다. |
@FunctionalInterface | 함수형 인터페이스라는 것을 알립니다. |
@Native | native메서드에서 참조되는 상수 앞에 붙입니다. |
@Target | 애노테이션이 적용가능한 대상을 지정하는데 사용합니다. |
@Documented | 애노테이션 정보가 javadoc으로 작성된 문서에 포함되게 합니다. |
@Inherited | 애노테이션이 자손 클래스에 상속되도록 합니다. |
@Retention | 애노테이션이 유지되는 범위를 지정하는데 사용합니다. |
@Repeatable | 애노테이션이 반복해서 적용할 수 있게 합니다. |
@Override
@Target이 METHOD로 메서드에만 붙일 수 있는 애노테이션으로, 조상의 메서드를 오버라이딩하는 것이라는걸 컴파일러에게 알려주는 역할을 합니다.
만약 @Override 애노테이션 없이 오버라이딩 하려고 할 때 조상메서드의 이름을 틀리게 입력하면 컴파일 에러 없이 넘어가게 됩니다. 그러면 컴파일러는 그냥 메서드가 추가된걸로 인식하지만 개발자 입장에서는 오류가 발생할 수 있는데 어디서 발생한 오류인지 쉽게 알아내기 어렵습니다.
아래의 코드는 Child가 Parent의 doSomething() 메서드를 재정의 하려고 했지만 이름을 실수로 입력해서 s가 3개나 더 들어가버렸습니다. 심한 고의성 트롤입니다. 하지만 컴파일러는 눈치채지 못하고 doSomethingsss() 라는 메서드를 새로 추가하는걸로 끝냅니다.
이런 상황에서 @Override 애노테이션을 사용하면, 컴파일러가 같은 이름의 메서드가 조상에 있는지 확인하고 없으면, 에러메시지를 출력해줍니다.
어느 클래스인지 왜 발생했는지 정말 친절하게 알려줍니다. 메서드를 오버라이딩할 때 @Override를 붙이는것은 필수가 아니지만, 알아내기 어려운 실수를 미연에 방지해주므로 조금 귀찮더라도 붙이도록 하는것이 안전한 코드를 만드는 방법입니다.
@Deprecated
새로운 버전의 JDK가 소개될 때, 새로운 기능이 추가도리 뿐만 아니라 기존의 부족했던 기능들을 개선하기도 합니다.
이 과정에서 기존의 기능을 대체할 것들이 추가되어도, 이미 여러곳에서 사용할지도 모르기때문에 함부로 삭제했다간 일부 기능에 마비가 올 수 있습니다. 그래서 나온 애노테이션이 @Deprecated 입니다.
- 더이상 사용되지 않는 필드나 메서드에 사용합니다.
- 이 애노테이션이 붙은 대상은 다른 것으로 대체되었으니 더 이상 사용하지 않는것을 권장한다는 의미입니다.
- @Deprecated가 있는 대상은 추가로 개선된 기능이 있거나 심각한 오류를 발생시킬 수 있으니 사용하지 않아야 합니다.
만약 @Deorecated 가 있는 대상을 코드에 사용하여 컴파일 하면 다음과 같은 결과가 발생합니다.
여기서는 Thread 클래스의 stop() 메서드를 사용했습니다.
경고메시지의 내용은 해당 소스파일이 'deprecated' 된 대상을 사용하고 있으며, '-Xlint:deprecation'옵션을 붙여서 다시 컴파일하면 자세한 내용을 알 수 있다는 뜻입니다.
'-Xlint:deprecation' 옵션을 붙여서 컴파일하면 자세한 내용을 보여줍니다.
@FunctionalInterface
'함수형 인터페이스(functional interface)' 를 선언할 때, '@FunctionalInterface'를 붙이면 컴파일러가 '함수형 인터페이스'를 올바르게 선언했는지 확인하고, 잘못된 경우, 에러를 발생시킵니다.
@Override 처럼 필수는 아니지만 붙이면 실수를 방지할 수 있으므로 '함수형 인터페이스'를 선언할 때는 이 애노테이션을 붙이는게 좋습니다.
대표적인 함수형 인터페이스로 얼마전에 배운 Runnable 인터페이스가 있습니다.
다음은 @FuntionalInterface 애노테이션을 사용하고 메서드가 0개, 1개, 2개 일때의 결과입니다.
만약 메서드 개수가 1개가 아니면 위와같은 에러메시지가 나옵니다. 역시나 컴파일러는 친절합니다.
@SuppressWarnings
컴파일러가 보여주는 경고 메시지를 나타나지 않게 억제해줍니다.
- 경고가 발생하면 해결하고 다시 컴파일해서 메시지를 나타나지 않게 하는것이 일반적이지만 특수한 경우에 경고가 발생할 것을 알면서도 묵인해야할 때가 있습니다.
- 이 경우 컴파일 때마다 경고 메시지가 나오기 때문에 '@SuppressWarnings'를 사용해서 경고 메시지가 나오지 않게 할 수 있습니다.
@SuppressWarnings의 토큰은 다음과 같이 있습니다.
토큰 | 설명 |
all | 모든 경고를 억제합니다. |
boxing | 오퍼레이션과 관련된 경고를 억제합니다. |
cast | 캐스트 오퍼레이션과 관련된 경고를 억제합니다. |
dep-ann | 권장되지 않는 애노테이션과 관련된 경고를 억제합니다. |
deprecation | 권장되지 않는 기능과 관련된 경고를 억제합니다. (@Deprecated) |
fallthrough | switch문에서 누락된 break 문과 관련된 경고를 억제합니다. |
finally | 리턴되지 않는 마지막 블록과 관련된 경고를 억제합니다. |
hiding | 변수를 숨기는 로컬과 관련된 경고를 억제합니다. |
incomplete-switch | switch 문에서 누락된 항목과 관련된 경고를 억제합니다.(enum case) |
javadoc | javadoc 경고와 관련된 경고를 억제합니다. |
null | 널(null) 분석과 관련된 경고를 억제합니다. |
rawtypes | 원시 유형 사용법과 관련된 경고를 억제합니다. |
restriction | 올바르지 않거나 금지된 참조 사용법과 관련된 경고를 억제합니다. |
resource | 닫기 가능 유형의 자원 사용에 관련된 경고 억제 |
serial | 직렬화 가능 클래스에 대한 누락된 serialVersionUID필드와 관련된 경고를 억제합니다. |
static-access | 잘못된 정적 액세스와 관련된 경고를 억제합니다. |
static-method | static으로 선언될 수 있는 메서드와 관련된 경고를 억제합니다. |
super | 슈퍼 호출을 사용하지 않는 메서드 오버라이딩과 관련된 경고를 억제합니다. |
synthetic-access | 내부 클래스로부터의 최적화되지 않은 액세스와 관련된 경고를 억제합니다. |
sync-override | 동기화된 메서드를 오버라이드하는 경우 누락된 동기화로 인한 경고 억제 |
unchecked | 미확인 오퍼레이션과 관련된 경고를 억제합니다. |
unqualified-field-access | 규정되지 않은 필드 액세스와 관련된 경고를 억제합니다. |
unused | 사용하지 않은 코드 및 불필요한 코드와 관련된 경고를 억제합니다. |
필요한 토큰은 경고 메시지를 보면 힌트가 있습니다.
경고메시지를 보면 -Xlint:deprecation 이라고 되있는 부분이 있습니다. 여기서 deprecation을 @SuppressWarnings의 토큰으로 사용하면 즉, @SuppressWarnings("deprecation") 으로 사용하면 해당 경고가 억제됩니다.
@SafeVarargs
메서드에 선언된 가변인자의 타입이 비 구체화 타입(non-reifiable type)일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 "unchecked" 경고가 발생합니다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 @SafeVarargs를 사용해야 합니다.
비 구체화 타입(non-reifiable type) : 타입 소거자에 의해 컴파일 타임에 타입 정보가 사라지는 것(런타임에 구체화하지 않는것)
@SafeVarargs는 static이나 final이 붙은 메서드와 생성자에만 붙일 수 있습니다.
즉, 오버라이드될 수 있는 메서드에는 사용할 수 없다는 뜻입니다.
예를들어 java.util.Arrays의 asList()는 다음과 같이 정의되어 있으며, 이 메서드는 매개변수로 넘겨받은 값들로 배열을 만들어서 새로운 ArrayList 객체를 만들어서 반환하는데 이 과정에서 경고가 발생합니다.
- asList()의 매개변수가 가변인자인 동시에 제네릭 타입입니다.
- 메서드에 선언된 타입 T는 컴파일 과정에서 Object로 바뀝니다.
- 즉 Object[]가 됩니다.
- Object[]에는 모든 타입의 객체가 들어있을 수 있으므로
- 이 배열로 ArrayList<T>를 생성하는 것은 위험하다고 경고하는 것입니다.
- asList()가 호출되는 부분을 컴파일러가 체크해서 타입 T가 아닌 다른 타입이 들어가지 못하게 할 것이므로 위의 코드는 아무런 문제가 없습니다. 따라서 @SafeVarargs로 경고를 억제해줘야 합니다.
- @SafeVarargs를 사용하면 'unchecked' 경고도 억제할 수 있습니다. 또한 호출하는 곳에서 발생하는 경고도 억제됩니다. (SuppressWarnings("unchecked")는 선언 뿐만 아니라 호출되는 곳에도 애노테이션을 붙여야합니다.
마커 애노테이션(Marker Annotation)
값을 지정할 필요가 없는 경우, 애노테이션의 요소를 하나도 정의하지 않을 수 있습니다.
이런 애노테이션을 마커 애노테이션 이라고 합니다.
대표적인 마커 애노테이션으로 @Override가 있습니다.
애노테이션 프로세서
(참고 강의 : www.inflearn.com/course/the-java-code-manipulation/dashboard 인프런 - 더 자바, 코드를 조작하는 다양한 방법)
- 소스코드 레벨에서 소스코드에 붙어있는 애노테이션 정보를 읽어와
- 컴파일러가 컴파일 중에 새로운 소스코드를 생성하거나 기존의 코드 변경을 가능하게 합니다.
- (코드 변경은 비추)
- 클래스 즉, 바이트 코드도 생성가능
- 소스코드와 별개의 리소스도 생성가능
대표적인 예제
- 롬복(Lombok)
- @Getter, @Setter, @Builder
- AutoService : java.util.ServiceLoader용 파일 생성 유틸리티
- 리소스 파일을 만들어줍니다.
- META-INF 밑의 service 밑에 ServiceLoader용 레지스트리 파일을 만들어줍니다.
- @Override
- 컴파일러가 오버라이딩하는 메서드가 잘못된 대상임을 체크해주는것도 애노테이션 프로세서
- Dagger2 : 컴파일 타임 DI 제공 -> 런타임 비용이 없어집니다.
- 안드로이드 라이브러리
- ButterKnife : @BindView (뷰 아이디와 애노테이션 붙인 필드 바인딩)
- DeepLinkDispatcher : 특정 URI 링크를 Activity로 연결할 때 사용
애노테이션 프로세서의 장점
- 컴파일 시점에 조작하기 때문에 런타임 비용이 제로!
애노테이션 프로세서의 단점
- 기존의 코드를 고치는 방법 -> 현재로써는 public 한 api가 없습니다.
참고문헌
- 자바의정석 3판
- docs.oracle.com/javase/tutorial/java/annotations/index.html (java tutorial - Annotations)
- docs.oracle.com/javase/8/docs/technotes/guides/language/annotations.html
- docs.oracle.com/javase/8/docs/api/java/lang/annotation/Annotation.html
- ktko.tistory.com/entry/Java%EC%9D%98-SuppressWarnings-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
- www.inflearn.com/course/the-java-code-manipulation/dashboard (인프런 - 더 자바, 코드를 조작하는 다양한 방법)
'java-liveStudy' 카테고리의 다른 글
14주차. 제네릭 (1) | 2021.03.01 |
---|---|
13주차. I/O (0) | 2021.02.15 |
11주차. Enum (0) | 2021.01.24 |
10주차 과제. 멀티쓰레드 프로그래밍 (0) | 2021.01.17 |
9주차 과제. 예외 (0) | 2021.01.16 |
댓글