-
[whiteship] 온라인스터디 - 12주차. 애노테이션IT 발자취.../JAVA 2021. 2. 6. 22:35
github.com/whiteship/live-study/issues/12
12주차 과제: 애노테이션 · Issue #12 · whiteship/live-study
목표 자바의 애노테이션에 대해 학습하세요. 학습할 것 (필수) 애노테이션 정의하는 방법 @retention @target @documented 애노테이션 프로세서 마감일시 2021년 2월 6일 토요일 오후 1시까지.
github.com
목표
자바의 애노테이션에 대해 학습하세요.
학습할 것 (필수)
- 애노테이션 정의하는 방법
- @retention
- @target
- @documented
- 애노테이션 프로세서
애노테이션
어노테이션은 주석이라는 뜻을 가지고 있다.
기본적으로 우리가 아는 주석은 // 또는 /**/ 이렇게 생겼는데, 어노테이션과 일반적인 주석은 뭐가 다를까?
- 어노테이션의 역할도 주석과 크게 다르지 않는다.
- 일반 주석과 큰 차이점은 코드를 작성할 수 있다는 것이 다르다.
- 코드를 작성할 수 있다는 뜻은 어노테이션으로 뭔가를 할 수 있다는 뜻이 된다.
- 어노테이션도 enum과 마찬가지로 1.5에 등장했다.
애노테이션을 정의하는 방법
public @interface Make {}
애노테이션의 조상을 확인한다. 바이트 코드로 확인해 본다.
$ javac Make.java $ javap -c Make.class Compiled from "Make.java" public interface com.gintire.pure.week12.Make extends java.lang.annotation.Annotation { }
확인해보면 java.lang.annotation.Annotation이 조상인 것을 확인할 수 있다.
애노테이션은 인터페이스로 구성되어있기 때문에 굳이 구현할려면 implements를 하던가 익명클래스로 만들어야한다.
Annotation annotation = new Annotation() { @Override public Class<? extends Annotation> annotationType() { return null; } }
애노테이션은 리플렉션 기술을 이용해서 사용된다는 것을 짐작할 수 있다.
애노테이션 요소의 규칙
- 요소의 타입은 기본형, String, enum, 애노테이션, Class만 허용된다.
- ()안에 매개변수는 선언할 수 없다.
- 예외를 선언할 수는 없다.
- 요소를 타입 매개변수로 정의 할 수 없다.표준 애노테이션
자바에서 제공하는 애노테이션은 크게 2가지로 구성되어있다.
하나는 자바 코드를 작성할 때 사용되는 애노테이션이고, 다른 하나는 애노테이션을 정의하기 위해 필요한 것들이다.
@Override
오버라이드를 할 때 사용되며, 메소드가 오버라이드 되었는지 알려주는 역할을 한다.
오버라이드는 아래와 같이 생겼다.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Deprecated
사용되지 않을 대상에 붙여진다.
애노테이션이 존재하는 이유는 버전이 올라가면서, 더 이상 사용이 되지 않는 것들이 존재한다.
예를 들어, Date클래스를 확인해 본다.
/** * Allocates a {@code Date} object and initializes it so that * it represents midnight, local time, at the beginning of the day * specified by the {@code year}, {@code month}, and * {@code date} arguments. * * @param year the year minus 1900. * @param month the month between 0-11. * @param date the day of the month between 1-31. * @see java.util.Calendar * @deprecated As of JDK version 1.1, * replaced by {@code Calendar.set(year + 1900, month, date)} * or {@code GregorianCalendar(year + 1900, month, date)}. */ @Deprecated public Date(int year, int month, int date) { this(year, month, date, 0, 0, 0); }
Date 클래스를 보면 생성자가 deprecated가 존재한다는 것을 확인할 수 있다.
주석을 읽어보면 Calendar 클래스를 사용하도록 권장한다.
이는 이전 버전과의 호환성 때문에 삭제 처리하지 않고 남겨두고 해당 애노테이션을 사용한다.
애너테이션 코드를 확인해본다.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated { /** * Returns the version in which the annotated element became deprecated. * The version string is in the same format and namespace as the value of * the {@code @since} javadoc tag. The default value is the empty * string. * * @return the version string * @since 9 */ String since() default ""; /** * Indicates whether the annotated element is subject to removal in a * future version. The default value is {@code false}. * * @return whether the element is subject to removal * @since 9 */ boolean forRemoval() default false; }
애너테이션 코드를 확인하면 구현부는 없다고 확인할 수 있다.
- 애너테이션은 인터페이스로 되어있기 때문에 구현이 되지 않는 것을 알 수 있다.
- default, static은 작성되지 않는다.
@FunctionalInterface
이 애노테이션은 이 인터페이스는 무조건 함수형으로 사용한다는 뜻이다.
함수형으로 작성할 때는 몇가지 규칙이 존재하는데, 그 중에 인터페이스는 단 하나의 메소드만 존재해야한다.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
@SuppressWarnings
컴파일러가 보여주는 경고 메시지가 보이지 않게 억제 해준다고 한다.
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { /** * The set of warnings that are to be suppressed by the compiler in the * annotated element. Duplicate names are permitted. The second and * successive occurrences of a name are ignored. The presence of * unrecognized warning names is <i>not</i> an error: Compilers must * ignore any warning names they do not recognize. They are, however, * free to emit a warning if an annotation contains an unrecognized * warning name. * * <p> The string {@code "unchecked"} is used to suppress * unchecked warnings. Compiler vendors should document the * additional warning names they support in conjunction with this * annotation type. They are encouraged to cooperate to ensure * that the same names work across multiple compilers. * @return the set of warnings to be suppressed */ String[] value(); }
@Target
애노테이션이 적용 가능한 대상을 지정하는데 사용된다.
위에서 Override를 다시 확인해본다.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
메소드에서 사용되어진다고 한다. 즉, 다른 필드 변수라던지, 생성자, 파라미터 등에서는 사용하다.
메소드말고, 타입, 필드, 애노테이션 타입, 생성자, 지역 변수, 모듈, 패키지, 파라미터, 파입 파라미터, 타입 유즈에 사용할 수 있다고 한다.
@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(); }
@Retention
애노테이션이 유지되는 범위를 지정하는데 사용된다.
Override를 다시 확인해 본다.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
확인결과 런타임까지 유지가 된다고 한다.
종류는 다음과 같다.
@documented
애노테이션 프로세서
리뷰
애너테이션은 주석이다.
런타임 중에 알아내야하는 값은 들어갈 수 없다.
컴파일러 수준에서 해석되고 완전히 정적이어야 한다.
- 문서화도 해야하므로 정적이어야 한다 ( javadoc )
Retention
- 언제까지 유지할 것인가.
- 기본값 : CLASS
- 반드시 값을 입력해줘야한다.
- SOURCE : 소스코드에만 유지하겠다. 컴파일하고 나면 어노테이션이 사라진다. ( == 주석으로만 쓰겠다. 바이트코드에 남아있지 않다. )
- 컴파일 할 때만 사용하겠다. ( 눈으로 보는용으로 )
- 예 > Override => Override 해야하는데 안했을 때, 컴파일러는 먼저 확인하고 알림을 줌 and 개발자가 확인
- CLASS : 어노테이션에 대한 정보를 바이트 코드에도 남겨두겠다.
바이트코드를 확인하면 애노테이션을 확인할 수 있다.
클래스 정보를 클래스 로더가 불러올 때 Retention이 CLASS면 누락 시킨다.
- RUNTIME :
클래스파일에 남겨져 있다면, 리플렉션이 가능해진다.
클래스 정보에 들어가 있기 때문에, 런타임 중에 클래스 정보를 확인할 수 있다.
클래스로더가 불러오면서
바이트 코드로 불러오는게 빠르냐, 리플렉션으로 불러오는게 빠르냐?? 정답은 모름
리플렉션은 부하가 있다.
리플랙션 비용 : 클래스 로더가 읽어오는 부하, 메모리에 적재하는 비용,
바이트코드 비용 : 바이트 코드의 크기에 따라 다름
어노테이션이 런타임까지 사용될 것인지, 사용되는 레벨 확인 필요
내가 사용하는 애너테이션에 Inherited가 없다면, .getSuperclass()를 사용하면된다.
public class Main { public static void main(String[] args) { Annotation[] annotations = ChildTest.class .getSuperclass() .getAnnotations(); for (int i = 0; i < annotations.length; i++) { Annotation annotation = annotations[i]; System.out.println(annotation.toString()); } } }
.getDeclaredAnnotations()를 사용하면, inherited 애너테이션을 무시한다. (== 실제 해당 클래스에 선언된 애너테이션만 가져온다.)
.getField() : public 필드만 가져옴
.getDeclaredField() : 모두 가져옴
public class Main { public static void main(String[] args) { Field[] fields = Test.class .getDeclaredFields(); for (Field field : fields) { System.out.println(field.toString()); } } }
getter / setter 가지고 싸우지말자 - 필드 가져오려면 다 가져올 수 있다.
좋은 질문 : 상속관계에서 자식 클래스에서 getField / getDeclaredField 일 때 결과
getField는 외부에서 볼 수 있는 값만 가져온다. 하지만 getDeclaredField하면 다 가져올 수 있다.
애너테이션 프로세스 - 일반 개발자는 거의 만들일이 없다.
알면 좋다.
서비스 로더 >
인터페이는 내가 만들어도 구현체는 다양한 곳에서 구현된다.
내가 지정하지 않고 jar만 바꿔서 사용할 수 있다.
jar 파일에 들어있는 구현체를 가져오는 것
구현체를 모르고 구현체를 사용하는 방법
META-INF/services 폴더에 jar
===> spring boot에서 spring.factories
애너테이션에서 쓰는 value를 쓰면은 어떻게 되는지 알아야한다.
'IT 발자취... > JAVA' 카테고리의 다른 글
[whiteship] 온라인스터디 - 11주차 과제 : Enum (0) 2021.01.30 [whiteship] 온라인스터디 - 8주차.인터페이스 (0) 2021.01.09 [whiteship] 온라인스터디 - 6주차. 상속 (0) 2020.12.26 [whiteship] 온라인스터디 - 5주차. 클래스 (0) 2020.12.19 [whiteship] 온라인스터디 - 4주차. 제어문 (0) 2020.12.12 댓글