본문 바로가기

프로그래밍/Java

Annotation

반응형

해당 포스트 작성의 목적

자바의신1 ch17 어노테이션에 대해 공부한다. 이미 공부한바 있지만, 멘토링을 진행한 경험을 되살려, 좀더 깊게, 그리고 왜? 라는 물음과 함께 어노테이션이란 무엇인지 알아본다.

Annotation이란?

클래스나 메소드등의 선언시에 '@'를 사용하는것을 말한다.

이 어노테이션은 다음과 같은 일을 행한다.

  • 컴파일러에게 정보를 알려주거나
  • 컴파일할 떄와 설치(deployment)시의 작업을 지정하거나
  • 실행할 떄 별도의 처리가 필요할 때

즉 프로그램에게 추가적인 데이터를 제공해주는

메타데이터의(데이터를 위한 데이터, 즉 데이터에 대한 설명을 의미) 역할

을 수행한다.

Annotation의 종류

자바에서 기본적으로 정한 어노테이션 3개와, 개발자가 직접 어노테이션을 선언하기 위해 사용되는 메타어노테이션4개가 존재한다.

기본 어노테이션 -@Override,@Deprecated,@SupressWarnings

@Override

해당 메소드가 부모클래스에 있는 메소드를 오버라이드 했다는 것을 명시적으로 나타낸다.

만약 자식클래스에서 메소드가 여러개 있다면, 어떤 메소드가 부모클래스로부터 상속받아 재정의 한것인지 한눈에 들어오기 쉽지않다. 이런 가독성과, 어노테이션을 지정해 둠으로써, 상속받은 메소드의 인자나 명에 오탈자로 인한 오류를 컴파일러가 체크해줄수있다.

@Deprecated

클래스나 메소드가 더이상 사용되지 않음을 의미한다. 해당 어노테이션이 선언된 클래스나, 메소드를 사용할 경우, 컴파일러에서 '경고로그'를 출력한다. 개발자는 이런 경고로그를 확인하고, 코드 리펙토링을 통해 순차적으로 프로그램을 수정해나갈수 있다. 쓰지 않는다고 하여 갑자기 사용중이던 클래스나 메소드를 없애버리거나 변경하면, 그 클래스와 메소드를 사용하는 다른 곳에서 갑작스러운 에러를 발생하게 된다. 이런 상황을 막기위해, Deprecated어노테이션으로 해당 클래스와 메소드를 사용하는 개발자들에게 충분히 리펙토링할 시간을 주는것이다.

@SupressWarnings

선언한 클래스/메소드의 컴파일 경고를 무시하도록 설정한다.

간혹 경고로그가 출력되어도 개발자가 이미 알고있거나, 일부러 의도한 경우 해당 어노테이션을 선언함으로써, 경고로그 출력을 막을 수있다.

Java7,8에서 추가된 기본 어노테이션들

앞서 말한 3가지의 어노테이션 외에 7과8에서 추가된 기본제공 어노테이션들이 있어 소개한다.

@SafeVarargs

Java7부터 지원되며, 제너릭 같은 가변인자의 매개변수를 사용할 때의 경고를 무시한다.

(설명을 들으니 SupressWarnings의 가변인자 매개변수 경고로그 전용 버전 같은 느낌이다.)

@FunctionalInterface

Java8부터 지원하며, 함수형 인터페이스를 지정하는 어노테이션이다.

만약 메소드가 존재하지 않거나, 1개이상의 메소드(default)가가 존재할 경우 컴파일 오류를 발생 시킨다. (@Override 처럼 FunctionalInterface를 명시적으로 선언해 컴파일러가 체크를 해줄수있다.)

💡
Java8부터 함수형 프로그래밍을 지원하면서 추가된 것으로, FunctionalInterface란 단 하나의 추상 메소드를 가지는 메소드를 말한다.(Default 메소드 제외) 추후에 시간이 되면 좀 더 알아보자!Functional Interface란

어노테이션을 개발자가 직접 선언하기 위한 메타어노테이션들

메타어노테이션이란, 기본적으로 정해진 어노테이션들 외에, 개발자가 추가적으로 직접 선언하여 사용할 경우 사용되는 어노테이션들을 말한다.

  • @Target
  • @Retention
  • @Documented
  • @inherited

가 있으며 이제부터 하나씩 알아가보자.

@Target

선언하고자 하는 어노테이션이 어떤 곳에 적용될지 선언하는 어노테이션이다.

@Target(ELementType.METHOD) // 해당 어노테이션은 메소드에 선언할 것을 선언

이처럼 ()안에 적용대상을 지정하는데, 다음과 같은 매개변수를 넣을 수 있다.

ElementType

요소타입 대상
Constructor 생성자 선언시
FIELD enum상수를 포함한 필드(field)값 선언 시
LOCAL_VATIABLE 지역변수 선언시
METHOD 메소드 선언시
PACKAGE 패키지 선언시
PARAMETER 매개 변수 선언시
TYPE 클래스, 인터페이스, enum등 선언시

@Retention

얼마나 오래 어노테이션 정보가 유지되는지 선언하는 어노테이션이다. 자바컴파일러가 어노테이션을 다루는 방법을 기술하며, 특정 시점까지 영향을 미치는지에 대해 기술한다. @Target과 마찬가지로 () 안에 시점을 매개변수로 넣을 수 있다.

@Retention(RetentionPolicy.RUNTIME) //실행 시 어노테이션 정보를 참조가능하다.

RetnetionPolicy

이름 대상
SOURCE 어노테이션 정보가 컴파일시 사라진다.
CLASS 클래스 파일에 있는 어노테이션 정보가 컴파일러에 의해 참조 가능하다. 하지만 JVM에서는 사라진다.
RUNTIME 실행시 어노테이션 정보가 가상 머신에 의해 참조가능하다.(Reflection 사용)

@Documented

해당 어노테이션에 대한 정보가 Javadocs(API) 문서에 포함된다는 것을 선언한다.

별로 알게 없다...

@Inherited

모든 자식클래스에서 부모 클래스의 어노테이션을 사용 가능하다는 것을 선언한다.

@interface

💡
추가적으로 알아야할 어노테이션 1가지 더! @interface 이다. 이 어노테이션은 class, interface 등과 같이 어노테이션을 선언하겠다는 키워드 이다.

Java8에서 추가된 메타어노테이션

@Repeatable

Java8부터 지원되며, 어노테이션을 연속적으로 사용할수 있도록 해준다.

어노테이션을 직접 선언해보자!

자, 이제 어노테이션을 선어할때 쓰이는 메타어노테이션들을 알아보았으니, 직접 선언해보도록 하자.

선언된 어노테이션

package gojExam;
import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
	 String data() default "default";
	
}

선언된 어노테이션을 쓰는 클래스

package gojExam;

public class UserAnnotationSample {
	 @UserAnnotation(data = "MHLab") //myData의 값은 "MHLab"으로 초기화 된다.
	    private String myData;

	    @UserAnnotation()
	    private String defaultData;
			/*
			아무런 값을 넣지 않았으나,어노테이션 선언시 default값을 지정 했으므로,
			해당 값이 들어간다.
			*/
	    public UserAnnotationSample() {
	        myData = "No data";
	        defaultData = "No data";
	    }

	    public String getMyData() {
	        return myData;
	    }

	    public String getDefaultData() {
	        return defaultData;
	    }
}

선언된 어노테이션의 값들을 확인해 보자!

위의 코드에서 저장된 어노테이션 값들을 확인해보자, 크게 사용할 일은없지만, 알아두면 좋다.

package gojExam;

import java.lang.reflect.Field;

public class UserAnnotationChecck {

	public static void main(String[] args) {
		UserAnnotationChecck us = new UserAnnotationChecck();
		us.checkAnnotations(UserAnnotationSample.class);

	}
	public void checkAnnotations(Class useClass) {
		Field[] fields = useClass.getDeclaredFields();
		for(Field f : fields) {
			UserAnnotation annotation = f.getAnnotation(UserAnnotation.class);
			if(annotation!=null) {
				String data = annotation.data();
				System.out.println(f.getName() +" " +data);
			} else {
				System.out.println(f.getName() + "annotation is null");
			}
		}
	}

}
/*
myData MHLab
defaultData default
*/

여기서 잠깐, Class,Field 등은 자바의 기본 API중 하나인 Reflection에서 제공하는 것이다.

Reflection API는 클래스나 메소드, 변수등을 가져올수 있는 API로, 대표적으로 인텔리제이, 이클립스등의 자동완성기능에서 Reflection 기능을 사용해서 해주고 있다. 알고만 가도록 하자.

포스트를 작성하며 알게된 점

어노테이션자체 보다는 Reflection API에 대해서 조금 알게되었다. 대표적으로 IDE들의 자동완성 기능에 쓰인 다는것도 알게 되었으며, Reflection API를 이용해 3rd파티의 라이브러리의 private 변수 값도 소스코드 수정없이 변경이 가능하다는 사실을 알게되었다. 지금은 이렇게 존재 하는것만 알고 넘어가지만, 추후에 반드시 다시 보게 될것같은 생각이 든다.

Reference

자바의신1

Java에서 어노테이션(Annotation) 이란?

Functional Interface란

Java에서 커스텀 어노테이션(Annotation) 만들고 사용하기

 

반응형

'프로그래밍 > Java' 카테고리의 다른 글

GC  (0) 2021.05.18
JAVA의 역사와 JVM-1  (0) 2021.05.18
Static 변수의 메모리 해제-Weak/Soft Reference  (0) 2021.05.17
클래스안의 클래스-2  (0) 2021.05.17
GC Log보는 방법  (0) 2021.05.13