- 어노테이션이란?
1.1 어노테이션
어노테이션은 컴파일 과정에서 코드를 어떻게 컴파일할 것인지 실행 과정에서 코드를 어떻게 처리할 것인지를 알려주는 정보입니다. 즉 메타 데이터로써 역할을 합니다. 메타데이터는 데이터를 위한 설명을 의미하는 데이터가 담겨있습니다.
주로 컴파일러에게 코드 문법 에러를 검사하도록 정보를 제공하거나 코드를 자동으로 생성할 수 있도록 정보를 제공합니다. 런타임 시에는 특정 기능을 실행하도록 정보를 제공합니다.
사용방식은 @ + 어노테이션 명으로 특정 클래스나 메소드, 변수에 붙여 사용합니다.
1.2 Java에서 제공하는 어노테이션
@Override
- 선언한 메소드가 오버라이드 되었다는 것을 나타냅니다.
- 상위 클래스에서 해당 메소드를 찾을 수 없으면 컴파일 에러를 발생 시킵니다.
@Deprecated
- 해당 메소드가 더 이상 사용되지 않음을 표시합니다.
- 사용할 경우 컴파일 경고를 발생 시킵니다.
@SuppressWarnings
- 선언한 곳의 컴파일 경고를 무시합니다.
@SafeVarargs
- Java7 부터 지원하고, 제너릭 같은 가변인자의 매개변수를 사용할 때 경고를 무시합니다.
@FunctionalInterface
- Java8 부터 지원하고, 함수형 인터페이스를 지정하는 어노테이션입니다.
- 메소드가 존재하지 않거나, 1개 이상의 메소드가 존재할 경우 컴파일 오류를 발생 시킵니다.
간단한 예제를 작성하여 위의 어노테이션을 사용해보겠습니다.
public class item {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public String printInfo() {
String info = "이름 : " + name + "\n"
+ "가격 : " + price + "\n";
return info;
}
@Deprecated
public void chagePrice(int price) {
this.price = price;
}
}
가격은 최초에 등록한 가격 이외에 변동을 제한한다고 가정했습니다. 해당 메소드를 사용하지 않게 사용자에게 알려주기 위해 @Deprecated를 붙였습니다.
해당 메소드를 사용하지 말라고 경고하는 것을 확인할 수 있습니다. 경고만 할 뿐, 무시하고 사용은 가능합니다.
pulibc class Book extends Item {
private String author;
private String publisher;
public Book(String name, int price, String author, String publisher) {
super(name, price);
this.author = author;
this.publisher = publisher;
}
@Override
public String printInfo() {
String info = super.printlnfo();
info += "저자 : " + author + "\n"
+ "출판사 : " + publisher + "\n";
return info;
}
}
printInfo는 아이템의 정보를 반환하는 메소드이다. Book 클래스에 추가로 들어온 필드가 있기 때문에 메소드를 재정의하였고, 상위 클래스에 printInfo 메소드가 있는지 @Override를 통하여 확인할 수 있었습니다.
public class Main {
public static void main(String[] args) {
Item item1 = new Item("커피", 3_800);
Book book1 = new Book("오브젝트", 38_000, "조영호", "위키북스");
Book book2 = new Book("객체지향의 사실과 오해", 20_000, "조영호", "위키북스");
item1.changePrice(1000); // 경고를 무시하고 강제로 사용
System.out.println(item1.printInfo()); // Item 클래스의 printInfo 사용
System.out.println(book1.printInfo()); // 오버라이딩된 printInfo 사용
System.out.println(book2.printInfo()); // 오버라이딩된 printInfo 사용
}
}
@SuppressWarnings 어노테이션에 "deprecaton" 속성을 넣으면 사용하지 말아야 하는 메소드 관련 경고를 억제하기 때문에 메소드 관련 경고를 억제하기 때문에 changePrice의 컴파일 경고가 무시된다! 그 밖에도 다양한 속성은 이곳에서 확인할 수 있습니다.
@FuctionalInterface는 Runnable Interface에서 확인할 수 있었습니다.
해당 인터페이스가 함수형 인터페이스임을 암시합니다. 해당 인터페이스에 메소드가 존재하지 않거나 두개 이상 존재하면 컴파일 에러를 발생시킵니다.
확인해보기 위해 함수형 인터페이스를 임의로 생성해보았습니다.
두개 이상의 메소드를 선언하면 컴파일 에러가 발생한 것을 확인할 수 있었습니다.
- 어노테이션 정의하는 방법
어노테이션 타입을 정의하기 위해서는 @interface를 사용하여 정의합니다.
public @interface CustomAnnotation {
}
어노테이션은 Element라는 것을 멤버로 가질 수 있습니다. Element는 타입과 이름으로 구성되며 default 값을 가질 수 있습니다. Element의 이름 뒤에는 메소드를 작성하는 것처럼 뒤에 ()를 붙여야 합니다.
public @interface CustomAnnotation {
// Element
String value();
String description();
}
이렇게 정의한 어노테이션은 아래 처럼 사용할 수 있습니다.
@CustomAnnotation(value = "25", description = "올해의 나이")
디폴트 값이 없는 경우에는 키를 반드시 기술해야 합니다.
기본 Element는 value이며, 해당하는 값은 @어노테이션(값)으로 바료 사용 가능합니다.
[요소가 하나이고, 이름이 value일 때 요소의 이름을 생략을 할 수 있습니다.]
public @interface CustomAnnotation {
// Element
String value();
}
@CustomAnnotation("25")
- @retention
어노테이션 선언에 사용되는 메타 어노테이션입니다. Java 컴파일러가 어노테이션을 다루는 방법이 기술되어 있습니다.
(어떤 시점까지 어노테이션이 영향을 미치는지 결정합니다.)
어느 시점까지 영향을 미치는지 결정합니다. 해당 정책들은 java.lang.annotaion.RetentionPolicy에 enum으로 정의되어 있습니다.
@Documented
@Retenton(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
publlic @interface Retention {
/**
* Returns the retention policy
* @return the retention policy
*/
RetentionPolicy value();
}
- RetentionPolicy.SOURCE : 컴파일 전까지 유효합니다. 컴파일 이후에는 사라집니다.
- RetentionPolicy.CLASS : 컴파일러가 클래스를 참조할 때 까지 유효합니다.
- RetentionPolicy.RUNTIME : 리플렉션을 이용하여 런타임시에도 어노테이션 정보를 얻을 수 있습니다.
(레플렉션은 Runtime 시에도 클래스의 메타 정보를 얻는 기능을 말합니다.)
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy
@Retention(RetentionPolicy.CLASS)
public @interface CustomAnnotaion {
// Element
String value();
Strng description();
}
컴파일러가 클래스를 참조할 때까지 유효하도록 정책을 설정하였습니다.
- @target
어노테이션 선언에 사용되는 메타 어노테이션입니다. 어노테이션을 적용할 수 있는 범위를 정의합니다.
java.lang.annotaton.ElementType에 다양한 범위가 정의되어 있습니다.
- ElementType.Type : 클래스, 인터페이스, 열거 타입에 적용합니다.
- ElementType.ANNOTATION_TYPE : 어노테이션에 적용합니다.
- ElementType.FIELD : 필드에 적용합니다.
- ElementType.CONSTRUCTOR : 생성자에 적용합니다.
- ElementType.METHOD : 메소드에 적용합니다.
- ElementType.LOCAL_VARIABLE : 로컬 변수에 사용합니다.
- ElementType.PACKAGE : 패키지에 사용합니다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
어노테이션이 적용될 대상을 지정할 때 사용합니다. @Target의 기본 Element는 ElementType 배열의 값을 가질 수 있습니다. 또한 필드명이 value이기 때문에 필드명 생략이 가능합니다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface CustomAnnotation {
// Element
String description();
}
위 어노테이션은 메소드와 필드에 사용할 수 있게 대상을 한정합니다.
public class Item {
@CustomAnnotation(description = "item 의 이름을 저장하는 name 변수")
private String name;
@CustomAnnotation(description = "item 의 가격을 저장하는 price 변수")
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public String printInfo() {
String info = "이름: " + name + "\n"
+ "가격: " + price + "\n";
return info;
}
@CustomAnnotation(description = "price 의 값을 변경하는 메소드")
public void changePrice(int price) {
this.price = price;
}
}
변수와 메소드에 붙여서 사용이 가능합니다.
- @Documented
어노테이션 선언에 사용되는 메타 어노테이션입니다.
javadoc 및 기타 문서툴에 의해 문서화될 때, 해당 애노테이션이 문서에 표시된다.
(문서에도 어노테이션의 정보가 표현됩니다.)
- 어노테이션 프로세스
참고 : better-dev.netlify.app/java/2020/09/07/thejava_16/
어노테이션 프로세스는 자바 컴파일러 플로그인의 일종으로, 어노테이션에 대한 코드베이스를 검사, 수정, 생성하는 역할을 합니다.
어노테이션을 사용하기 위해서는 어노테이션 프로세스가 필요합니다.
동작구조
- 어노테이션 프로세스를 사용한다는 것을 자바 컴파일러가 알고 있는 상태에서 컴파일을 수행합니다.
- 어노테이션 프로세스들이 각자의 역할에 맞게 구현되어 있는 상태에서 실행되지 않은 어노테이션 프로세스를 실행합니다.
- 어노테이션 프로세스 내부에서 어노테이션에 대한 처리를 합니다.
- 자바 컴파일러가 모든 어노테이션 프로세스가 실행되었는지 검사하고, 모든 어노테이션 프로세스가 실행되지 않았다면 반복합니다.
어노테이션을 이용하여 프로세스를 처리하는 것을 의미합니다. 특히 컴파일 단계에서 어노테이션에 정의된 액션을 처리하는데, 이것은 어플리케이션이 실행되기 전에 체킹을 해주기 때문에 어노테이션이 의도한대로 이루어지지 않으면 눈으로 확인할 수 있는 에러나 경고를 보여줍니다.
어노테이션 프로세스의 가장 대표정인 예로는 Lombok 라이브러리가 있습니다.
getter/setter 와 같이 반복적이고 중복되는 많은 코드를 양산하는 코드(boilerplate code)를 최소화하고 핵심적인 기능을 명확하게 확인할 수 있게 도와줍니다.
참고
'JAVA' 카테고리의 다른 글
제네릭(Generic) (0) | 2021.02.27 |
---|---|
I/O (Input/Output) (0) | 2021.02.20 |
Enum (0) | 2021.01.25 |
멀티쓰레드 프로그래밍 (0) | 2021.01.19 |
자바의 예외처리 (0) | 2021.01.16 |