안녕하세요. 명월입니다.
이 글은 Java에서 어노테이션 (Annotation) 사용법에 대한 글입니다.
어노테이션은 클래스나 메서드, 변수 등의 메타 데이터, 데이터의 정의를 설정하는 데이터라고 할 수 있습니다.
즉, 어노테이션의 데이터로는 실제 데이터를 적용하는 것은 아니고, 클래스, 메서드, 변수를 구분하기 위한 데이터라고 할 수 있습니다.
어노테이션은 일단 골뱅이 마크(@)로 설정합니다.
Copy! [소스 보기] Example.java// 람다식용 인터페이스를 설정하는 어노테이션 (메서드를 두개 선언하면 에러 발생) @FunctionalInterface interface LambdaExpression{ void run(); } class Example { // 재정의 어노테이션 (Object까지 상속) @Override public String toString() { return "Hello world"; } // 사용 금지를 명시하는 어노테이션 @Deprecated public void print() { System.out.println("print"); } // 경고를 없애주는 어노테이션 @SuppressWarnings("unused") public void test() { int aaa = 0; } }
정말 자주 사용하는 어노테이션으로는 Override, Deprecated가 있습니다.
Override는 클래스를 상속받고 재정의를 명시할 때 사용하는 어노테이션입니다. Deprecated는 메소드를 삭제할 수는 없고(기존에 사용하던 클래스였는데), 사용을 권장하지 않음을 알릴 때 사용하는 어노테이션입니다.
Deprecated는 이클립스에서 취소선이 생겨서 확실히 사용하면 안된다는 모습을 보여줍니다. 그러나 사용한다고 에러가 발생하는 건 아닙니다.
여기까지가 기본 Java의 어노테이션이고 이번에는 어노테이션을 만들어 보겠습니다.
Copy! [소스 보기] Example.javaimport java.lang.annotation.ElementType; import java.lang.annotation.Target; // 클래스에서 사용하도록 설정 @Target(ElementType.TYPE) //어노테이션 선언 @interface TestClassAnnotaion { // 어노테이션 속성 (value는 속성 설정을 생략 가능하다.) public String value(); } // 메서드에서 사용하도록 설정 @Target(ElementType.METHOD) // 어노테이션 선언 @interface TestMethodAnnotaion { // 어노테이션 속성 public String name() default "no name"; } // 변수에 사용하도록 설정 @Target(ElementType.FIELD) //어노테이션 선언 @interface TestVariableAnnotaion { // 어노테이션 속성 default가 없으면 필수 속성이 된다. public String name(); } public class Example { // 클래스 어노테이션 설정 @TestClassAnnotaion("test") class Node { // 메서드 어노테이션 @TestMethodAnnotaion(name = "print") public void print() { // 콘솔 출력 System.out.println("Hello world"); } } @TestVariableAnnotaion(name = "node") private Node node = null; public Example() { // Node 인스턴스 생성 node = new Node(); // print함수 호출 node.print(); } // 시작 함수 public static void main(String... args) { new Example(); } }
어노테이션은 특별히 프로그램 실행하는 데는 영향이 없습니다. 단지 메타 데이터로써 좀 더 여러가지 클래스와 함수, 변수등에 구분할 수 있는 정보의 활용으로써 사용이 가능합니다.
어노테이션을 생성할 때, Target으로 어노테이션의 사용 구분을 정할 수 있습니다.
타입 | 설명 |
---|---|
@Target | |
TYPE | 클래스, 인터페이스, enum에 설정 |
FIELD | 멤버 변수에 설정 |
METHOD | 메서드에 설정 |
PARAMETER | 함수의 파라미터에 설정 |
CONSTRUCTOR | 생성자에 설정 |
LOCAL_VARIABLE | 로컬 변수에 설정 |
ANNOTATION_TYPE | 어노테이션에 설정 |
PACKAGE | 패키기에 설정 |
TYPE_PARAMETER | 제네릭 타입에 설정 |
PACKAGE | 모든 곳에 설정 |
@Retention | |
SOURCE | 소스상에서만 사용되는 메타 정보 |
CLASS | .class 파일까지는 남지만, Runtime에서는 보이지 않음 |
RUNTIME | Runtime시에도 참조 가능함. Reflection을 통해 클래스나 메서드의 구분을 할 수 있음 |
그 밖에 @Inherited는 클래스 상속 시에 어노테이션도 상속받을 수 있습니다. @Documented는 자바 독을 생성할 때, 메타 데이터를 포함하는 어노테이션입니다.
사실 개인적인 생각으로는 Reflection이 아니면 어노테이션을 사용할 일은 크게 많지 않을 것 같습니다.
Reflection에서는 클래스나 메소드를 찾는 역할로 어노테이션을 사용합니다.
Copy! [소스 보기] Example.javaimport java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; // 메서드에서 사용하는 어노테이션 @Target(ElementType.METHOD) // Reflection에서 찾기 위한 어노테이션 @Retention(RetentionPolicy.RUNTIME) // 어노테이션 생성 @interface CallMethod { // 어노테이션 속성 public String name() default "no name"; } // 클래스 class Example { // Runtime 어노테이션 설정 @CallMethod public void run1() { // 콘솔 출력 System.out.println("run1 call"); } // 어노테이션 설정 안함 public void run2() { // 콘솔 출력 System.out.println("run2 call"); } // Runtime 어노테이션 설정 @CallMethod public void run3() { // 콘솔 출력 System.out.println("run3 call"); } // 실행 함수 public static void main(String[] args) throws Throwable { // 인스턴스 생성 Example ex = new Example(); // Reflection에서 메서드 검색 for(Method method : ex.getClass().getMethods()) { // CallMethod의 어노테이션 취득 CallMethod anno = method.getAnnotation(CallMethod.class); // 어노테이션 설정이 없으면 null if(anno != null) { // CallMethod 어노테이션이 있는 함수가 실행 method.invoke(ex); } } } }
위 에제를 보면 Reflection에서 어노테이션을 설정한 함수만 호출하고 있습니다.
즉, 어노테이션으로 전략 패턴(Strategy pattern)이나 facade 패턴을 구현하는게 가능합니다.
그 외에 이클립스 같은 IDE 툴이나 Jankins 같은 CI툴에서 데이터를 주고 받기 위한 어노테이션입니다.
주석을 위한 어노테이션을 작성하는 것이라면 오히려 가독성이 떨어져서 소스가 지저분해 보이니 주석과 어노테이션은 확실히 구분해서 사용하는 게 좋습니다.
여기까지 Java에서 어노테이션 (Annotation) 사용법에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.
'Study > Java' 카테고리의 다른 글
[Java] 25. Object 클래스 (notify, wait 사용법) (4) | 2020.05.27 |
---|---|
[Java] 24. 동기화(synchronized) 그리고 교착 상태(Deadlock) (0) | 2020.05.26 |
[Java] 23. 스레드풀(Threadpool)를 사용하는 방법 (0) | 2020.05.25 |
[Java] 22. 스레드(Thread)를 사용하는 방법 (0) | 2020.05.25 |
[Java] 20. iterator(for-each)과 Stream API (0) | 2020.05.20 |
[Java] 19. 람다식(Lambda)를 사용하는 방법 (0) | 2020.05.19 |
[Java] 18. 익명 클래스(Anonymous class)와 클로저(closure) (0) | 2020.05.18 |
[Java] 17. 제네릭 타입(Generic type) (0) | 2020.05.15 |