애너테이션
애너테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공한다.
애너테이션의 등장 배경
기존에는 소스코드와 관련 문서를 따로 작성하여 소스코드 변경시 문서 내용도 변경해야 했지만 그러지 않는게 빈번하게 발생했다. 이로 인해 소스코드와 문서의 불일치 발생하여 소스코드에 주석을 넣어 문서와 소스코드를 합쳐 하나로 만들게 되었다.
- javadoc.exe 프로그램이 소스파일에서 주석을 추출해 문서를 작성
/**
* The common interface extended by all annotation interfaces. Note that an
* interface that manually extends this one does <i>not</i> define
* an annotation interface. Also note that this interface does not itself
* define an annotation interface.
*
* More information about annotation interfaces can be found in section
* {@jls 9.6} of <cite>The Java Language Specification</cite>.
*
* The {@link java.lang.reflect.AnnotatedElement} interface discusses
* compatibility concerns when evolving an annotation interface from being
* non-repeatable to being repeatable.
*
* @author Josh Bloch
* @since 1.5
*/
// /** ~ */ : javadoc.exe를 위한 주석
// 소스코드와 소스코드에 대한 설명
public interface Annotation {
...
마찬가지로 소스코드와 설정파일(XML)을 따로 작성하여 소스코드와 문서와 같이 문제가 빈번하게 발생했다. 이를 해결하기 위해 문서와 설정에 대한 정보를 합쳐서 작성한 것이 애너테이션이다.
설정파일(XML)은 여러 사람이 공유해야하는 경우 동시에 수정할 수 없는 불편함이 있음 애너테이션을 사용하면 각자 자기 프로그램에 설정정보를 애너테이션으로 추가하면 된다.
애너테이션은 특정 프로그램을 위한 정보로 예를 들어 @Test는 Junit이라는 특정 프로그램에 정보 제공(설정 정보)을 위한 것이라는 의미
- Junit : Java 단위 테스트 프로그램
@Test // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
}
표준 애너테이션
Java에서 제공하는 애너테이션
@Override : 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.
- 오버라이딩시 메서드 이름을 잘못적는 실수를 하는 경우가 많다.
- 오버라이딩시 메서드 선언부에 붙여야 한다.
class Parent {
public void method() {}
}
class Child extends Parent{
@Override // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {}
}
@Deprecated : 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
@Deprecated
public int getDate() {
return normalize().getDayOfMonth();
}
@FuncthionalInterface : 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크
- 함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있음
- 함수형 인터페이스임을 알리는 효과를 가진다.
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@SuppressWarnings : 컴파일러의 경고메시지가 나타나지 않게 억제한다.
- 괄호()안에 억제하고자하는 경고의 종류를 문자열로 지정한다.
- 컴파일러가 알려준 경고를 확인하고 문제가 없음을 알리는 효과가 있다.
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Object obj = new Object();
ArrayList list = new ArrayList();
list.add(obj);
}
- 둘 이상의 경고를 동시에 억제할 수 있다.
@SuppressWarnings({"경고종류1", "경고종류2","경고종류3"}) // 배열과 같이 중괄호 사용
메타 애너테이션
메타 에너테이션은 '애너테이션을 위핸 애너테이션'으로 애너테이션을 만들때 사용한다.
메타 애너테이션은 java.lang.annotation패키지에 포함되어있다.
@Target : 애너테이션을 정의할 때, 적용 대상 지정에 사용한다.
- @SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@Retention : 애너테이션이 유지(retention)되는 기간을 지정하는데 사용
RUNTIME은 실행시까지, CLASS는ClassLoar까지 (ClassLoar : 클래스 파일을 메모리에 올려주는 역할)
- 컴파일러에 의해 사용되는 애너테이션의 유지 정택책은 SOURCE이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 실행시에 사용 가능한 애너테이션의 정책은 RUNTIME이다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {
@Documented
- javadoc으로 작성한 문서에 포함시키려면 @Documented를 붙인다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {
@Inherited
- 애너테이션을 자손 클래스에 상속하고자 할 때,
@Inherited
@Target(ElementType.TYPE)
@interface SuperAnno{}
@SuperAnno
class Parent {}
// Child 클래스에 @SuperAnno 애너테이션이 붙은 것으로 인식
class Child extends Parent{}
@Repeatable
- 반복해서 붙일 수 있는 애너테이션을 정의할 때 사용
@Repeatable(ToDos.class) // 애너테이션을 여러 번 반복해서 쓸 수 있게 만든다.
@interface ToDo {
String value();
}
- @Repeatable인 애너테이션을 하나로 묶을 컨테이너 애너테이션을 정의해야한다.
@interface ToDos { // 여러 개의 ToDo애너테이션을 담을 컨테이너 애너테이션
ToDo[] value(); // ToDo애너테이션 배열타입의 요소를 선언, 이름이 반드시 value여야 함;
}
- @Repeatable인 애너테이션을 반복해서 붙일 수 있다.
@ToDo("one")
@ToDo("two")
애너테이션 타입 정의하기
애너테이션을 직접 만들어 쓸 수 있다.
@interface 애너테이션이름 {
타입 요소이름(); // 애너테이션의 요소를 선언한다.
...
}
애너테이션의 요소인 메서드는 추상 메서드이며, 애너테이션을 적용할 때 지정한다(순서X)
enum TestType{FIRST, FINAL}
@interface TestAnnotation {
int testNum();
}
@interface Test {
int id = 101; // static final이 생략된 상수
int count();
String value();
String[] values();
TestType testType(); // enum
TestAnnotation testA(); // 자신이 아닌 다른 애너테이션을 포함할 수 있다.
}
추상 메서드를 구현할 필요 없이 정의된 요소의 타입과 이름에 맞게 값을 지정해야 한다.
@Test(
count = 0, value ="10",
values= {"1","2","3","4","5","6","7","8","9","10"},
testType = TestType.FIRST,
testA = @TestAnnotation(testNum=9)
)
class TestClass {}
애너테이션 적용시 요소 값을 지정하지 않아도, 사용될 수 있는 기본값을 지정할 수 있다.
@interface Test {
int value() default 1; // 기본값을 1로 지정
}
// 적용시 요소값 지정 생략 가능
@Test // @Test(value = 1)과 동일
class TestClass {}
애너테이션의 요소가 하나이고 이름이 value일 때는 요소의 이름을 생략할 수 있다.
@interface Test {
int value();
}
// 적용시 요소의 이름 생략 가능
@Test(10)
class TestClass {}
요소의 타입이 배열인 경우 중괄호{}를 사용한다.
@Test(value = 10)
@Test({10,20}) // 요소의 이름이 value이기 때문에 생략 가능
@Test(value={}) // 값이 없을 때는 반드시 중괄호{} 필요
모든 애너테이션의 조상 (Java.lang.annotation.Annotation)
모든 애너테이션은 Annotation을 상속받는다. Annotation은 인터페이스로 equals(), hashCode(), toString(), annotationType() 메서드를 가지는데 애너테이션의 요소와 같이 추상 메서드를 구현하지 않아도 모든 애너테이션은 해당 메서드를 사용할 수 있다. 컴파일러가 자동으로 추상 메서드들을 구현해 주기 때문이다.
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
마커 애너테이션(Marker Annotation)
요소가 하나도 정의되지 않은 애너테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
애너테이션이 붙어있는 것 만으로 사용하는 프로그램에 정보를 준다.
class Child extends Parent{
@Override
public void methodA() {
System.out.println("methodA");
}
}
애너테이션 요소의 규칙
애너테이션의 요소를 선언할 때 아래의 규칙을 반드시 지켜야 한다.
- 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용됨
- 요소의 괄호() 안에 매개변수를 선언할 수 없다.
- 요소에 예외를 선언할 수 없다.
- 요소를 타입 매개변수로 정의할 수 없다
클래스로부터 애너테이션을 읽어오기
'Java' 카테고리의 다른 글
[JAVA] 쓰레드의 동기화(synchronization) (0) | 2024.01.18 |
---|---|
[JAVA] 쓰레드(thread) (0) | 2024.01.18 |
[JAVA] 열거형(enum) (0) | 2024.01.05 |
[JAVA] 제네릭(Generics) (0) | 2023.11.25 |
[JAVA] Collections 클래스 (0) | 2023.11.24 |