DEV/Java

[java] 어노테이션(Annotation)의 이해와 사용방법

Bi3a 2023. 10. 17. 04:05

목차
반응형

Java 어노테이션의 이해와 사용 방법
java 기초 깨부시기

     

     


     

    java 어노테이션이란?

    Annotation : 주석이라는 사전적 의미를 가지고 있습니다.
    java에서 어노테이션은 @를 붙여 코드에서 주석처럼 쓰이며, 동시에 컴파일 및 실행 시 여러 기능을 제공합니다.
    사용자뿐만 아니라 프로그램에 추가 정보를 제공할 수 있으며, 이를 '메타 데이터'라고 부릅니다.

     

    java 어노테이션의 기능과 효과

    java 어노테이션은 아래와 같은 기능과 효과를 제공합니다. 
    1. 메타 데이터 제공
      • 컴파일 혹은 실행 시 특정 코드에 따른 기능을 제어  `ex: deprecated, override`
        • 컴파일러에게 어노테이션에 따른 코드 작성에 대해 에러를 체크하거나 예외 처리
        • 런타임에 특정 기능을 실행할 수 있도록 제어
      • IDE 등에서 코드 작성 시 코드 자동 생성, 생성 보조 정보 제공 등   `ex: getter, setter`
    2. 코드를 읽는 사람으로 하여금 주석과 같이 해당 코드에 대한 설명 제공

     

     

    java 어노테이션의 종류

    java 어노테이션은 크게 3가지로 분류됩니다.
    1. 빌트인 어노테이션 : Java.lang 내장 어노테이션으로 작성한 코드에 대한 컴파일러 제어 기능
      • 종류 : `@Override, @Deprecated, @SuppressWarning, @FuntionalInterface`
    2. 메타 어노테이션 : 어노테이션 작성 및 정의를 위해 사용하는 어노테이션
      • 종류 : ` @Target,  @Retention, @Inherited, @Document, @Repeatable`
    3. 커스텀 어노테이션 : 사용자가 직접 작성하는 어노테이션

     

    1. 빌트인 어노테이션의 사용 방법과 구현 예제

    @Override

    `@Override` 어노테이션은 자식 클래스 메서드가 부모 클래스 메서드를 재정의함을 의미합니다.
    1. 사용 시 해당 메소드의 메서드명과 매개변수 속성이 오버라이딩에 적합한지 컴파일러에서 확인합니다.
    2. 부모 클래스의 메소드를 오버라이딩 하는 경우에는 반드시 어노테이션을 명시할 수 있도록 합시다.
      • 상위 클래스에서의 메소드의 오버로딩과 오버라이딩을 컴파일러가 정상적으로 구분할 수 있게끔 하기 위함 
    java
    닫기
    /* Override */
    class Parent{
    void method(){
    System.out.println("parent method");
    }
    }
    class Child extends Parent {
    @Override // 해당 인자값과 메소드명이 부모 클래스와 일치한지 컴파일러가 확인
    void method() {
    System.out.println("child method");
    }
    }

     

    @Override가 컴파일러 오류를 체크하는 경우

    java
    닫기
    /* Override */
    class Parent{
    void method(){
    System.out.println("parent method");
    }
    }
    class Child extends Parent {
    @Override // java: method does not override or implement a method from a supertype
    void method1() { // 메소드명이 불일치
    System.out.println("child method");
    }
    }

     

    @Deprecated

    더 이상 사용되지 않는다는 사전적 의미를 가진 어노테이션입니다.
    1. `@Deprecated` 어노테이션이 붙은 클래스, 메소드 등은 가급적 사용을 자제해 달라는 의미로 사용됩니다.
    2. `@Deprecated` 클래스나 메소드가 사용되거나 클래스를 상속, 메서드 오버라이드시 컴파일 과정에서 경고합니다.
    java
    닫기
    class Parent{
    @Deprecated
    void method(){
    System.out.println("parent method1");
    }
    }
    class Child extends Parent {
    @Override
    void method() { // warning : Overrides deprecated method in Parent
    System.out.println("child method1");
    }
    }

     

    deprecated alert가 발생한다.
    컴파일을 통해 deprecated 메소드를 오버라이드 한다고 경고합니다.

     

    @SuppressWarnings

    특정 컴파일 경고에 대해 인지하고 있으므로, 이를 사용하지 않도록 설정하는 것입니다.
    1. 두 가지 이상의 경고를 억제하기 위해서는 `@SuppressWarnings{("A", "B")}` 와 같이 묶어서 사용합니다.
    2. 경고를 억제하는 경우 그 이유에 대해 주석으로 부연 설명하는 것이 좋습니다.
    3. 대표적인 `@SuppressWarnings` 을 통한 경고 억제 종류
      • `@SupressWarnings("all")` : 모든 경고를 억제합니다.
      • `@SupressWarnings("deprecation")` : 사용하지 말아야 할 메서드 관련 경고를 억제합니다.
      • `@SupressWarnings("finally")` : 반환하지 않는 finally 블럭 관련 경고를 억제합니다.
      • `@SupressWarnings("unchecked')` : 검증되지 않은 연산자, 미확인 오퍼레이션 관련 경고를 억제합니다.
      • `@SupressWarnings("rawtypes')` : 클래스의 매개변수 타입이 불특정 시 관련 경고를 무시합니다.
      • `@SupressWarnings("unused')` : 미사용 코드 관련 경고를 무시합니다.
      • `@SupressWarnings("null")` : 널 관련 경고를 억제합니다.
      • `@SupressWarnings("cast")` : 형변환 관련 경고를 억제합니다.
    4. 구현 예시
    java
    닫기
    class Parent{
    @SuppressWarnings("unused") // 미사용 코드 경고 억제
    int unused; // 미사용 필드 unused
    }
    // ... 생략 ...
    public static void main(String[] args) {
    @SuppressWarnings("rawtypes") // 제너릭을 사용하는 클래스 타입 불특정 경고 억제
    ArrayList list = new ArrayList();
    }

     

    @FunctionalInterface

    인터페이스가 함수형 인터페이스임을 명시하며, 함수형 인터페이스 조건을 만족하는지 검증하는 어노테이션입니다.
    1. 함수형 인터페이스의 조건 : 인터페이스 내 한 개의 추상메소드만 존재해야 함
    2. 그래서 default나 static 메소드는 해당 인터페이스에 존재해도 상관없습니다. (비교적 지양할 것)
    3. 이러한 함수형 인터페이스는 익명 클래스 및 람다식 구현에 사용됩니다.
    java
    닫기
    @FunctionalInterface // error : 함수형 인터페이스에서 2개 이상의 추상 메소드 사용 불가
    interface FItest{
    void absmethod1();
    void absmethod2();
    }

     

    interface FItest{ // 함수형 인터페이스에 한 개의 추상메소드만 존재 가능하다
    void absMethod1();
    static void sttMethod(){ // static 메소드
    System.out.println("static method");
    }
    default void dftMethod(){ // default 메소드
    // 인터페이스 내부의 default 메소드는 명시적으로 default로 선언해야 한다
    System.out.println("default method");
    }
    }

     

     

    2. 메타 어노테이션별 커스텀 어노테이션 구현 방법과 예제

    메타 어노테이션은 어노테이션을 직접 커스터마이징하고 생성 할 수 있는 기능을 제공합니다.
    메타 어노테이션별 기능을 단계적으로 예제에 적용해보며 알아보겠습니다. 

     

    @interface

    @interface는 사용자가 정의한 커스텀 어노테이션임을 정의합니다.
    java
    닫기
    /* Custom Annotation */
    @interface CustomAnnotation {
    }

     

    @Target 

    @Target은 어노테이션을 적용할 수 있는 대상을 지정합니다.
    • 지정할 수 있는 옵션
      • 복수 지정 시 `@Target({ElementType.METHOD, ElementType.PARAMETER})` 와 같은 형태로 묶어 사용합니다.
      • `@Target(ElementType.TYPE)` : 기본값, 클래스의 어떤 요소에나 적용 가능
      • `@Target(ElementType.FIELD)` : 클래스의 필드
      • `@Target(ElementType.METHOD)` : 클래스의 메서드
      • `@Target(ElementType.PARAMETER)` : 메서드 인자값
      • `@Target(ElementType.TYPE_PARAMETER)` : 제네릭과 같은 타입 매개변수
      • `@Target(ElementType.CONSTRUCTOR)` : 클래스 생성자
      • `@Target(ElementType.LOCAL_VARIABLE)` : 클래스 지역변수
      • `@Target(ElementType.ANNOTATION_TYPE)` : 어노테이션 타입
    • 예제
    java
    닫기
    @Target({ElementType.METHOD, ElementType.FIELD}) // 지정할 수 있는 옵션을 메소드, 필드로 한정
    @interface CustomAnnotation {}
    @CustomAnnotation // error : 클래스는 지정할 수 있는 옵션이 아님
    class testTargetAnnotation {
    @CustomAnnotation
    int local;
    @CustomAnnotation
    void method(){};
    }

     

    @Retention

    @Retention은 어노테이션의 수명 주기를 지정합니다.
    • 수명 주기 짧은 순 → 긴 순 : SOURCE(. java 파일까지) → CLASS(. class 파일까지) → RUNTIME(실행 중에도)
    • 지정할 수 있는 옵션
      • `@Retention(RetentionPolicy.SOURCE)` :. java 소스 파일까지 어노테이션이 적용되게끔 설정합니다.
        • 컴파일 이후에는 사라집니다.
        • ex) `@Override, @Getter, @Setter` 등이 있습니다.
      • `@Retention(RetentionPolicy.CLASS)` : 컴파일 이후. class 파일까지 어노테이션이 적용되게끔 설정합니다.
        • 런타임에는 사라집니다.
        • ex) Lombok의 `@NonNull` 등이 있습니다.
      • `@Retention(RetentionPolicy.RUNTIME)`: 런타임에도 JVM에 남아 어노테이션이 참조될 수 있게 설정합니다.
        • ex) `@Deprecated` 등이 있습니다.

     

    @Documented

    @Documented은 javadoc에서 해당 어노테이션을 표기할지 여부를 결정합니다.

     

    • 인텔리제이에서 javadoc export 하는 방법
    1. `shift + shift (search anything)` 으로 들어간 뒤 Generate JavaDoc 검색 후 클릭
    2. File은 export 할 파일, 지정 디렉터리는 해당 패키지가 있는 디렉터리로 지정
    3. `Visibility Level` : 어느 제어자까지 javadoc에 표기할 것인지를 묻는 것이므로 확인하고자 하는 메서드 혹은 클래스의 선언부를 확인 후 기재
    4. `Command line Arguments` : `-encoding UTF-8 -charset UTF-8 -docencoding UTF-8` 기입\
    5. `Generate`로 생성 후 해당 디렉터리에서 `index.html` 실행

     

    • @Documented 적용 / 미적용 차이 확인
    java
    닫기
    /**
    * Documented을 적용 여부를 확인할 사용자 정의 어노테이션(현재는 미적용 상태)
    */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomAnnotation {
    String s();
    }
    /**
    * Document 적용 여부에 따른 JavaDoc 차이를 확인한다.
    */
    @CustomAnnotation(s = "document test")
    public static void testDocument() {
    }
    public static void main(String[] args) {
    testDocument();
    }

     

    (좌) @Documented 미적용 / (우) @Documented 적용

     

     

    Documented가 적용된 어노테이션을 사용 시 Javadoc에 사용된 어노테이션이 같이 표기됨을 확인했습니다.

     

     

    @Repeateable

    @Repeatable : 연속적으로 중복 정의가 가능하게끔 어노테이션을 사용할 수 있게 설정합니다.

     

    구현 예시 (Repeatable을 사용하지 않은 어노테이션의 value 값 복수 정의)

    java
    닫기
    public @interface CustomAnnotation {
    String[] value();
    }
    @CustomAnnotation({"Hi", "Hello", "How are you"})
    public static void testRepeatable(){
    }

     

    구현 예시 (Repeatable을 사용해 중복 어노테이션 사용을 통한 value 값 복수 정의)

    java
    닫기
    // Repetable을 적용할 어노테이션
    @Documented
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(CustomAnnotationContainer.class)
    // Repeatable을 적용하기 위해 위에서 만든 컨테이너.class를 반복한다고 @Repeatable 명시한다
    public @interface CustomAnnotation {
    String value() default "";
    }
    // 컨테이너 어노테이션 : 묶고자 하는 어노테이션을 배열로 멤버로 만든다
    // * 컨테이너 어노테이션의 메타 어노테이션 제한은 묶을 어노테이션과 동일해야 함
    @Documented
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomAnnotationContainer {
    CustomAnnotation[] value(); // 묶고자 하는 CustomAnnotation을 배열로 멤버 선언
    }
    // Repetable이 적용된 어노테이션의 사용 예시
    @CustomAnnotation("Hi")
    @CustomAnnotation("Hello")
    @CustomAnnotation("How are you")
    public static void testRepeatable(){}

     

    Q. 이렇게 따로따로 Repeatable을 사용해 value를 명시하는 이유가 뭐일까요?
    A. 어노테이션이 여러 값을 받는 속성인 경우 가독성 면에서 별도로 명시하는 것이 용이할 수 있기 때문입니다.
    또한 @Test (value1 = 1, value2 = "aaa", value 3 = false)와 같은 형태로 여러 멤버를 명시해야 한다면
    더더욱이 어노테이션을 중복 선언하는 것이 코드를 짜는 입장에서 보기 편할 것입니다.

     

     

    #구현 코드

    클릭 시 깃허브로 이동합니다.

    반응형