자바스터디 - 12주차

megaseunghan·2022년 8월 19일
0

자바스터디

목록 보기
12/15
post-thumbnail

목표

자바의 애노테이션에 대해 학습하세요.

학습할 것 (필수)

애노테이션이란

자바를 개발한 사람은 소스코드에 대한 문서를 만들기 보다, 소스 코드의 문서를 하나의 파일로 만드는 것이 더 낫다고 생각했다. 이로 인해 개발된 것이 JavaDoc - /** ~ */이다.

  • 이 기능을 응용하여 프로그램의 소스코드안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이 애노테이션이다.

  • 애노테이션은 사전적으로 주석이라는 의미를 가지고 있으며, 프로그램에 대한 데이터를 제공하는 메타 데이터의 한 형태이다.

java.lang.annotation.Annotation

모든 애노테이션의 조상은 Annotation이다. 그러나 애노테이션은 상속이 허용되지 않으므로 아래와 같이 명시적으로 Annotation을 조상으로 지정할 수 없다.

@interface TestCode extends Annotation { // 에러. 허용되지 않는 표현
    int count();
    String testedBy();
}

게다가 Annotation은 애노테이션이 아니라 일반적인 인터페이스로 정의되어 있다.

package java.lang.annotation;

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
	Class<? extends Annotation> annotationType();
}

모든 애노테이션 객체에 대해 equals(), hashCode(), toString() 과 같은 메서드를 호출하는 것이 가능하다.

애노테이션의 용도

  • 컴파일러에게 코드 작성 문법을 에러를 체크하고자, 에러 메세지를 억제하고자 사용
  • 소프트웨어 개발툴의 빌드, 배치 시점에서 자동으로 코드를 생성하도록 사용
  • 런타임 시점에서 특정 기능을 실행할 수 있도록 사용(Java Reflection)

애노테이션 종류

표준 애노테이션

애노테이션설명
@Override컴파일러에게 오버라이딩하는 메서드라는 것을 알린다.
@Deprecated앞으로 사용하지 않을 것을 권장하는 대상에 붙인다.
@SuppressWarnings컴파일러의 특정 경고메시지가 나타나지 않게 해준다.
@SafeVarargs지네릭스 타입의 가변인자에 사용한다.(JDK1.7)
@FunctionalInterface함수형 인터페이스라는 것을 알린다.
@Nativenative 메서드에서 참조되는 상수 앞에 붙힌다.(JDK1.8)

메타 애노테이션

애노테이션을 위한 애노테이션이다. 애노테이션을 정의할 때 애노테이션의 적용대상이나 유지기간 등을 지정하는데 사용된다.

애노테이션설명
@Target애노테이션이 적용가능한 대상을 지정하는데 사용한다.
@Documented애노테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
@Inherited애노테이션이 자손 클래스에 상속되도록 한다.
@Retention애노테이션이 유지되는 범위를 지정하는데 사용한다.
@Repeatable애노테이션을 반복해서 적용할 수 있게 한다.(JDK1.8)

마커 애노테이션

값을 지정할 필요가 없는 경우 애노테이션의 요소를 하나도 정의하지 않을 수 있다.

Serializable이나 Cloneable과 같은 요소가 하나도 정의되지 않은 애노테이션을 마커 애노테이션이라고 한다.

애노테이션 정의하는 방법

public @interface 애노테이션 이름 {
	타입 요소이름();
    ...
}

@interface 를 사용하여 애노테이션 타입을 선언한다.

애노테이션 타입

기호 @interface는 각자 별개이다. 그래서 둘 사이에 공백을 주어도 문제없이 작동된다. 근데 보기가 안좋다.

public @			interface 애노테이션 이름 {
    타입 요소이름();
    ...
}

애노테이션의 요소

애노테이션 내에 선언된 메서드를 애노테이션의 요소라고 한다.

  • 애노테이션의 요소는 반환값이 있고, 매개변수는 없는 추상메서드의 형태를 가진다.
  • 상속을 통해 구현하지 않아도 된다. 하지만 적용시 요소들의 값을 빠짐없이 적어야한다(순서는 상관하지 않는다.)
  • 예외를 선언할 수 없다.
  • default값을 지정할 수 있다. int count() default 3
  • 요소를 타입 매개변수(지네릭)로 정의할 수 없다.
  • 요소의 타입은 int, String, enum, 애노테이션, Class만 가질 수 있다.
  • 요소는 배열로 여러개를 가질 수 있으며, 만약 요소가 하나라면 요소명은 제외하고 값만 적어도 된다.(배열이여도 요소명이 value면 값만 적을 수 있다.)

아래 예제를 보며 요소의 특징을 활용한다.

  • myAnnotation
public @interface TestCheck {
    int count();	// Primitive Type 원시형
   	String testedBy();	// String
    String[] testTools();	// 배열
    TestType testType; // enum TestType { FIRST, SECOND }
    DateTime testTime(); // 자신이 아닌 애노테이션을(@DateTime) 포함시킬 수 있다.
}

// 애노테이션 적용 예시

@TestCheck(
	count = 3, testedBy = "seunghan",
    testTools = {"JUnit", "AutoTester"},
    testType = TestType.FIRST,
    testTime = @DateTime(yyyymmdd = "220202", hhmmss = "001234")
)
public class TestClass {
    ...
}

@Override

메서드 앞에만 붙일 수있는 애너테이션이다. 조상의 메서드를 오버라이딩하는 것이라는걸 컴파일러에게 알려주는 역할을 한다.

  • 예시
// 조상 클래스의 doOverriding 메서드
class Parent {
	public void doOverriding() {
    	...
	}
}
// 자손 클래스의 dooverriding 메서드 -> 메서드 명에 오타가 있다.
class Child extends Parent {
    public void dooverriding {
        ...
    }
}

위 코드처럼 Child 클래스가 부모인 Parent클래스의 doOverriding을 오버라이딩하려고 했으나 메서드명을 잘못 기입할 때, @Override가 없다면 컴파일러가 자손의 메서드명이 잘못된 것을 인식하지 못한다(그냥 자손 클래스에 새로운 메서드가 생겨나는 것이다).

@Override를 사용하면 다음과 같이 잘못된 것을 인식한다. 알아내기 어려운 실수를 미연에 방지할 수 있기 떄문에 반드시 붙히는게 좋다.

@Deprecated

새로운 버전의 JDK가 소개될 때, 기존의 부족했던 기능들을 개선하면서 그 기능을 새롭게 대체할 것이 추가된다. 하지만 어디서 쓰일지도 모르는 기존의 기능을 삭제할 수는 없다. 따라서 사용되지 않는 필드, 메서드에 @Deprecatede를 붙혀 더 이상 사용하지 않을 것을 권고한다.

만일 @Deprecated가 붙은 필드 || 메서드를 적용하고 컴파일 하면 다음과 같은 메시지가 나타난다.

Note: AnnotationXXX.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

-Xlint:deprecation : 다시 컴파일하면 상세한 내용을 알 수 있다.

@SuppressWarnings

컴파일러가 보여주는 경고메시지를 보이지 않게 억제한다. 경고 메시지에는 여러 종류가 있는데 JDK 버전이 올라갈 수록 계속 추가될 것이다.

억제 가능한 경고 메시지설명
deprecation@Deprecated가 사용된 필드 || 메서드를 사용시 발생하는 경고를 억제함
unchecked지네릭스로 타입을 지정하지 않았을 때 발생하는 경고를 억제함
rawtype지네릭스를 사용하지 않아서 발생하는 경고 억제함
varargs가변인자의 타입이 지네릭 타입일 때 발생하는 경고를 억제함

@SafeVarargs

  • reifiable - 컴파일 이후에 제거되지 않는 타입
  • non-reifiable - 컴파일 이후에 제거되는 타입 (지네릭 타입은 대부분 이에 해당함)

메서드에 선언된 가변인자의 타입이 non - reifiable 타입인 경우 해당 메서드를 선언 & 호출하는 부분에서 unchecked 경고가 발생한다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 붙힌다.

이 애노테이션은 static, final이 붙은 메서드와 생성자에만 사용이 가능하다. 오버라이딩에는 사용이 불가능하다는 뜻이다.

메서드를 선언할 때 이 애노테이션을 붙히면 호출할 때도 경고 메시지가 억제된다.

추가로 @SafeVarargs로 unchecked 경고를 억제할 수 있지만 varargs 경고는 억제할 수 없다. 따라서 @SuppressWarnings("varargs")를 습관적으로 같이 사용한다.

@Retention

애너테이션이 유지되는 기간을 지정하는데 사용된다. 애너테이션의 유지 정책의 종류는 다음과 같다.

유지 정책의미
SORUCE소스 파일에만 존재. 클래스파일에는 존재하지 않는다.
CLASS클래스 파일에 존재. 실행시에 사용불가. 기본값
RUNTIME클래스 파일에 존재. 실행시에 사용가능
  • SOURCE - @Override, @SuppressWarnings처럼 컴파일러가 사용하는 애너테이션은 SOURCE 정책을 사용한다. 컴파일러를 직접 작성할 것이 아니라면 이 유지정책은 필요없다.
  • CLASS - 컴파일러가 애너테이션의 정보를 클래스 파일에 저장할 수 있게는 하지만 JVM에 로딩될 떄는 애너테이션 정보가 무시되어 실행시 정보를 얻을 수 없다. 그래서 기본값임에도 잘 사용되지 않는다
  • RUNTIME - 실행시 리플렉션(reflection)을 통해 클래스 파일에 저장된 애너테이션의 정보를 읽어서 처리할 수 있다.

@Target

애너테이션이 적용가능한 대상을 지정하는데 사용된다. 여러 개의 값을 지정할 때는 배열처럼 {}을 사용한다.

  • @Target
@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();
}

ElementType을 배열로 적용가능한 대상으로 여러값을 줄 수 있다.

@Target(ElmentType.FIELD, ElementType.METHOD, ElementType.TYPE)

ElementType 종류

대상 타입의미
ANNOTATION_TYPE애너테이션
CONSTRUCTOR생성자
FIELD필드(멤버변수, enum 상수)
LOCAL_VARIABLE지역변수
METHOD메서드
PACKAGE패키지
PARAMETER매개변수
TYPE타입(클래스, 인터페이스, enum)
TYPE_PARAMETER타입 매개변수(JDK1.8)
TYPE_USE타입이 사용되는 모든 곳

@Documented

애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다. 자바에서 제공하는 기본 애너테이션 중 @Override, @SuppressWarnings을 제외한 모두 이 메타 애너테이션이 붙어있다(@Documented본인까지도).

package java.lang.annotation;

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

애노테이션 프로세서

애노테이션 프로세서란?

애노테이션을 프로세싱하는 기술로 자바 컴파일러 플러그인의 일종으로 애노테이션에 대한 코드베이스를 검사, 수정, 생성하는 역할을 가진다.

애노테이션 프로세서는 컴파일 시점에 끼어들어 특정한 애노테이션이 붙어있는 소스코드를 참조해서 새로운 소스코드를 만들어 낼 수 있는 기능이다. 이 때 새로 생성되는 소스코드는 자바일수도 있고 다른 코드일수도 있다.

애노테이션 프로세서 사용 예

  • 롬복(Lombok)
  • AutoService : java.util.ServiceLoader용 파일 생성 유틸리티
  • @Override
  • Dagger2 : 컴파일 타임 DI 제공

애노테이션 프로세서 장/단점

장점

  • 런타임 비용이 없다.

단점

  • 기존 클래스 코드를 변경할 때 약갼의 해킹이 필요하다.

0개의 댓글