주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공
애너테이션 사용 예
@Test // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.(이게 애너테이션) public void method() { ... }
애너테이션 | 설명 |
---|---|
@Override | 컴파일러에게 오버라이딩하는 메서드라는 것을 알린다. |
@Deprecated | 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다. |
@SuppressWarnings | 컴파일러의 특정 경고메시지가 나타나지 않게 해준다. |
@SafeVarargs | 지네릭스 타입의 가변인자에게 사용한다.(JDK1.7) |
@FunctionalInterface | 함수형 인터페이스라는 것을 알린다.(JDK1.8) |
@Native | native메서드에서 참조되는 상수 앞에 붙인다.(JDK1.8) |
@Target* | 애너테이션이 적용가능한 대상을 지정하는데 사용한다. |
@Documented* | 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다. |
@Inherited* | 애너테이션이 자손 클래스에 상속되도록 한다. |
@Retention* | 애너테이션이 유지되는 범위를 지정하는데 사용한다. |
@Repeatable* | 애너테이션을 반복해서 적용할 수 있게 한다.(JDK1.8) |
*가 붙은 것은 메타 애너테이션
오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.
오버라이딩할 때 메서드 이름을 잘못적는 실수를 하는 경우가 많다.class Parent { void parentMethod() {} } class Child extends Parent { void parentmethod() {} // 오버라이딩하려 했으나 이름을 실수로 잘못적음 }
이를 방지하기 위해
class Parent { void parentMethod() {} } class Child extends Parent { @Override void parentmethod() {} }
오버라이딩이 아니라고 알려줌.
앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
옛날꺼라 문제가 생길수도 있으니 웬만하면 사용하지 않는것을 권장하도록 @Deprecated
가 붙는다.
@Deprecated
public int getDate() {
return normalize().getDayOfMonth();
}
@Deprecated
가 붙은 대상이 사용된 코드를 컴파일하면 에러는 아니지만 경고가 나타난다.
함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크(14장에 있으니 자세한 설명은 생략)
함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있음@FunctionalInterface public interface Runnable { public abstract void run(); // 추상메서드 1개 }
컴파일러의 경고메시지가 나타나지 않게 억제한다.
괄오() 안에 억제하고자 하는 경고의 종류를 문자열로 지정@SuppressWarnings("unchecked") // 지네릭스와 관련된 경고를 억제 ArrayList list = new ArrayList(); // 지네릭 타입을 지정하지 않았음. list.add(obj); // 여기서 경고가 발생
여러개를 쓰고 싶으면
@SuppressWarnings({"unchecked", "deprecation"})
'-Xlint'옵션으로 컴파일하면, 경고메시지를 확인할 수 있다.
@SuppressWarnings를 쓰는 이유 - 경고를 이미 알고 있고 확인했다는 신호
애너테이션을 위한 애너테이션
메타 애너테이션은 java.lang.annotation 패키지에 포함
애너테이션 | 설명 |
---|---|
@Target | 애너테이션이 적용가능한 대상을 지정하는데 사용한다. |
@Documented | 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다. |
@Inherited | 애너테이션이 자손 클래스에 상속되도록 한다. |
@Retention | 애너테이션이 유지되는 범위를 지정하는데 사용한다. |
@Repeatable | 애너테이션을 반복해서 적용할 수 있게 한다.(JDK1.8) |
애너테이션을 정의할 때, 적용대상 지정에 사용
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
대상타입 | 의미 |
---|---|
ANNOTATION_TYPE | 애너테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드(멤버변수, enum상수) |
LOCAL_VARIABLE | 지역변수 |
METHOD | 메서드 |
PACKAGE | 패키지 |
PARAMETER | 매개변수 |
TYPE | 타입(클래스, 인터페이스, enum) |
TYPE_PARAMETER | 타입 매개변수(JDK1.8) |
TYPE_USE | 타입이 사용되는 모든 곳(JDK1.8) |
@Target({FEILD, TYPE, TYPE_USE}) // 적용 대상이 FIELD, TYPE, TYPE_USE
public @interface MyAnnotation {} // MyAnnotation을 정의
@MyAnnotation // 적용대상이 TYPE인 경우
class MyClass {
@MyAnnotation // 적용대상이 FIELD인 경우
int i;
@MyAnnotation // 적용대상이 TYPE_USE인 경우
MyClass mc;
}
애너테이션이 유지(retention)되는 기간을 지정하는데 사용
유지 정책 | 의미 |
---|---|
SOURCE | 소스 파일에만 존재. 클래스파일에는 존재하지 않음. |
CLASS | 클래스 파일에 존재. 실행시에 사용불가. 기본값 |
RUNTIME | 클래스 파일에 존재. 실행시에 사용가능. |
컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE이다.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
Override
는 오버라이딩을 했는가 체크하는 애너테이션이다. 이는 컴파일러가 체크하고 끝나서 실행할때는 필요 없기 때문에 SOURCE로 소스 파일에만 존재하게 하는 것이다.
실행시에 사용가능한 애너테이션의 정책은 RUNTIME이다.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
실행까지 간다.
javadoc으로 작성한 문서에 포함시키려면
@Documented
를 붙인다.
우리가 쓸일은 없기때문에 넘어감애너테이션을 자손 클래스에 상속하고자 할 때, @
Inherited
를 붙인다.@Ingerited // @SuperAnno가 자손까지 영향 미치게 @interface SuperAnno {} @SuperAnno class Parent {} class Child extends Parent {} // Child에 애너테이션이 붙은것으로 인식
둘다 많이 사용하는것은 아니라 알아두는 정도로만
반복해서 붙일 수 있는 애너테이션을 정의할 때 사용
@Repeatable(ToDos.class) @interface ToDo { String value(); } ---@Repeatable인 @ToDo를 하나로 묶을 컨테이너 애너테이션도 정의해야 함.--- @interface ToDos { // 여러개의 ToDo애너테이션을 담을 컨테이너 애너테이션 ToDos ToDo[] value(); // ToDo애너테이션 배열타입의 요소를 선언. 이름이 반드시 value이어야 함 } ---이렇게 하면 여러번 사용 가능--- @ToDo("delete test codes.") @ToDo("override inherited methods") class MyClass { ... }
많이 안쓰니 알아만 두자
애너테이션을 직접 만들어 쓸 수 있다.
@interface 애너테이션이름 { 타입 요소이름(); // 애너테이션의 요소를 선언한다. ... } @interface DateTime { String yymmdd(); String hhmmss(); }
@interface TestInfo {
int count();
String testedBy();
String[] testTools();
TestType testType(); // enum TestType {FIRST, FINAL}
DateTime testDate(); // 자신이 아닌 다른 애너테이션(@DateTime)을 포함할 수 있다.
}
--- 요소의 이름과 맞게 전부 설정해 줘야 한다. ---
@TestInfo(
count=3, testedBy="Kim",
testTools={"JUnit", "AutoTester"},
testType=TestType.FIRST,
testDate=@DateTime(yymmdd="160101", hhmmss="235959")
)
public class NewClass { ... }
적용시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null제외)
@interface TestInfo {
int count() default 1; // 기본값을 1로 지정
}
@TestInfo // @TestInfo(count=1)과 동일
요소가 하나이고 이름이 value일 때는 요소의 이름 생략가능
@interface TestInfo {
String value();
}
@TestInfo("passed") // @TestInfo(value="passed")와 동일
요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.
@interface TestInfo {
String[] testTools();
}
@Test(testTools={"JUnit", "AutoTester"})
@Test(testTools="JUnit")
@Test(testTools={}) // 값이 없을 때는 괄호{}가 반드시 필요
사실 Annotation은 인터페이스이다. 상속은 불가
package java.lang.annotation;
public interface Annotation { // Annotation자신은 인터페이스이다.
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extands Annotation> annotationType(); // 애너테이션의 타입을 반환
}
요소가 하나도 정의되지 않은 애너테이션
@Test // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
...
}
요소가 없으니까 그냥 저렇게 쓰면 된다. 마커 애너테이션은 그냥 말로 부르고 다니는것 딱히 의미는 없다.
요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용됨
괄호() 안에 매개변수를 선언할 수 없다.
예외를 선언할 수 없다.
요소를 타입 매개변수로 정의할 수 없다.(<T> 요런거)