안녕하세요. 명월입니다.
이 글은 Java에서 어노테이션 (Annotation) 사용법에 대한 글입니다.
어노테이션은 클래스나 메서드, 변수 등의 메타 데이터, 데이터의 정의를 설정하는 데이터라고 할 수 있습니다.
즉, 어노테이션의 데이터로는 실제 데이터를 적용하는 것은 아니고, 클래스, 메서드, 변수를 구분하기 위한 데이터라고 할 수 있습니다.
어노테이션은 일단 골뱅이 마크(@)로 설정합니다.
// 람다식용 인터페이스를 설정하는 어노테이션 (메서드를 두개 선언하면 에러 발생)
@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의 어노테이션이고 이번에는 어노테이션을 만들어 보겠습니다.
import 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에서는 클래스나 메소드를 찾는 역할로 어노테이션을 사용합니다.
import 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 |