본문 바로가기
☕ Java 웹 프로그래밍/Java

[프로그래머스] Java(자바) 중급 | Part 6. 어노테이션

by 일단연 2023. 5. 16.

 어노테이션 

어노테이션은 Java5에 추가된 기능

 

어노테이션

개념

  • ‘주석’이라는 의미를 가짐
  • 소스코드에 메타코드(추가정보)를 주는 것

문법

  • 클래스나 메소드 위에 붙여 사용
  • @(at)기호로 이름이 시작 (예: @Override)

용도

  • 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공
  • 소프트웨어 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보 제공
  • 실행시(런타임시) 특정 기능을 실행하도록 정보를 제공
    • 어노테이션을 클래스나 메타코드에 붙인 후, 클래스가 컴파일되거나 실행될 때 어노테이션의 유무나 어노테이션에 설정된 값을 통하여 클래스가 좀 더 다르게 실행되게 할 수 있음
    • 이런 이유로 어노테이션을 일정의 설정파일처럼 설명하는 경우도 있음

종류

  •  내장(표준) 어노테이션: Java가 기본으로 제공하는 어노테이션
  • 커스텀(Custom) 어노테이션:사용자가 직접 작성하는 어노테이션

 

내장(표준) 어노테이션

  • 7개의 표준 어노테이션 중에 3개가 java.lang의 일부이며, 나머지 4개는 java.lang.annotation으로부터 가져옴

  종류  

            자바 코드에 적용되는 내장 어노테이션 

  • @Override
    • 선언한 메서드가 오버라이드 되었다는 것을 나타냄
    • 만약 상위(부모) 클래스(또는 인터페이스)에서 해당 메소드를 찾을 수 없다면 컴파일 에러를 발생시킴
  • @Deprecated
    • 해당 메소드가 더 이상 사용되지 않음을 표시
    • 만약 사용할 경우 컴파일 경고를 발생시킴
  • @SuppressWarnings
    • 선언한 곳의 컴파일 경고를 무시
  • @SafeVarargs
    • Java7 부터 지원하며, 제너릭 같은 가변인자의 매개변수를 사용할 때의 경고를 무시
  • @FunctionalInterface
    • Java 8부터 지원하며, 함수형 인터페이스를 지정하는 어노테이션
    • 만약 메소드가 존재하지 않거나, 1개 이상의 메소드(default 메소드 제외)가 존재할 경우 컴파일 오류를 발생시킴

              기타 어노테이션에 적용되는 어노테이션 (메타 애터네이션)

  • @Retention
    • Java 컴파일러가 어노테이션을 다루는 방법을 기술하며, 특정 시점까지 영향을 미치는지를 결정
    • 종류
      • RetentionPolicy.SOURCE: 컴파일 전까지만 유효 (컴파일 이후에는 사라짐)
      • RetentionPolicy.CLASS: 컴파일러가 클래스를 참조할 때까지 유효
      • RetentionPolicy.RUNTIME: 컴파일 이후에도 JVM에 의해 계속 참조가 가능 (리플렉션 사용)
  • @Documented
    • 해당 어노테이션을 Javadoc에 포함시킴
  • @Target
    • 어노테이션이 적용할 위치를 선택
    • 종류
      • ElementType.PACKAGE: 패키지 선언
      • ElementType.TYPE: 타입 선언
      • ElementType.ANNOTATION_TYPE: 어노테이션 타입 선언
      • ElementType.CONSTRUCTOR: 생성자 선언
      • ElementType.FIELD: 멤버 변수 선언
      • ElementType.LOCAL_VARIABL: 지역 변수 선언
      • ElementType.METHOD: 메서드 선언
      • ElementType.PARAMETER: 전달인자 선언
      • ElementType.TYPE_PARAMETER: 전달인자 타입 선언
      • ElementType.TYPE_USE: 타입 선언
  • @Inherited
    • 어노테이션의 상속을 가능하게 함
  • @Repeatable
    • Java 8부터 지원하며, 연속적으로 어노테이션을 선언할 수 있게 해줌

 

커스텀 어노테이션

  • 개념: 사용자가 직접 작성하는 어노테이션
  • 이용 방법
    1. 어노테이션을 정의
    2. 어노테이션을 클래스에서 사용 (타겟에 적용)
    3. 어노테이션을 이용해 실행

 

커스텀 어노테이션 예시

 1. 어노테이션 정의 

  • Package Explorer에서 [new - Annotation]을 이용하여 Count100이라는 어노테이션 생성
    • 인터페이스와 비슷한 모양 But, 인터페이스 앞에 @가 붙어있음
    • Count100 어노테이션을 JVM 실행 시에 감지할 수 있게 하려면 Count100 어노테이션 위에 @Retention(RetentionPolicy.RUNTIME)를 붙여줘야 함
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Count100 {

}

 2. 어노테이션을 클래스에 사용 

  • "hello"를 출력하는 hello( )메소드를 가지는 MyHello라는 클래스를 작성
    • hello메소드 위에 @Count100 어노테이션을 붙임
public class MyHello {
  @Count100
  public void hello(){
    System.out.println("hello");
  }
}

 3. 어노테이션을 이용해 실행 

  • MyHello클래스를 이용하는 MyHelloExam클래스를 작성
    • MyHello의 hello메소드가 @Count100어노테이션이 설정되어 있을 경우, hello( )메소드를 100번 호출하도록 함

              1) MyHello의 객체 생성 

MyHello hello = new MyHello();

              2) 메소드에 대한 정보를 얻어낼 수 있는 코드를 작성 

  • MyHello 클래스의 hello( )메소드 정보를 알아내기 위해 메소드 타입으로 객체 생성 > method는 hello( )메소드에 대한 정보를 갖게 됨
  • Method는 java.lang.reflect.Method 클래스임
  • getClass( )메소드
    • java.object가 갖고 있는 메소드
    • 해당 인스턴스를 만들 때 사용한 클래스의 정보를 리턴
  • getDeclaredMethod( )메소드
    • 해당 클래스에 대한 정보를 얻고 그 정보로부터 hello라는 이름의 메소드에 대한 정보를 리턴
Method method = hello.getClass().getDeclaredMethod("hello");

              3) getDeclaredMethod( )메소드는 Exception 처리를 해야 함 

  • catch (NoSuchMethodException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    • 자동완성으로 try-catch문을 작성하면 예외가 2개 작성되지만, NoSuchMethodException과 SecurityException는 Exception을 상속받기 때문에 Exception 하나로 처리할 수 있음
MyHello hello = new MyHello();

try{
  Method method = hello.getClass().getDeclaredMethod("hello");}
}catch(Exception e){
  ex.printStackTrace();
} 

              4) try문 안에 hello( )메소드를 100번 호출하는 if문 작성 

  • MyHello의 Hello( )메소드에 Count100 어노테이션이 적용되어 있다면 for문으로 이동해, MyHello의 Hello( )메소드가 100번 실행됨
  • 적용되어 있지 않다면 else문으로 이동해, MyHello의 Hello( )메소드가 딱 한 번만 실행됨
  • method가 가지고 있는 isAnnotationPresent( )메소드
    • 특정 어노테이션이 메소드에 적용되어 있는지를 boolean 타입으로 리턴
    • Class에서 호출했을 때 부모클래스에 적용된 경우도 true를 리턴
if(method.isAnnotationPresent(Count100.class)){
  for(int i = 0; i < 100; i++){
    hello.hello();
  }
}else{
  hello.hello();
}

 

 완성 코드 

import java.lang.reflect.Method;

public class MyHelloExam {
  public static void main(String[] args) {
    MyHello hello = new MyHello();

    try{
      Method method = hello.getClass().getDeclaredMethod("hello");
      if(method.isAnnotationPresent(Count100.class)){
        for(int i = 0; i < 100; i++){
          hello.hello();
        }
      }else{
        hello.hello();
      }
    }catch(Exception e){
      ex.printStackTrace();
    }       
  }
}

 

실습

  • 문제 설명
    • 어노테이션은 메소드가 특정한 방식으로 동작하도록 표시하는데 쓸 수 있는데요. @RunTwice 어노테이션을 AnnotationExam의 원하는 메소드에 붙여보고 [제출]을 눌러보세요. 해당 메소드가 두 번 실행되는 걸 확인할 수 있습니다.
  • 해설
    • 메소드1~3 중 원하는 메소드 위에 @RunTwice 어노테이션을 붙이면 해당 메소드가 2번 호출됨
    • 1. 어노테이션을 정의
      • 어노테이션을 JVM실행시에 감지할 수 있도록 하려면 @Retention(RetentionPolicy.RUNTIME)를 붙여줘야 합니다.
    • 2. 어노테이션을 클래스에서 사용
      • 타겟 메소드 위에 @RunTwice 어노테이션을 붙힙니다.
    •  3. 어노테이션을 이용하여 실행
      • AnnotationExam의 타겟 메소드가 @RunTwice어노테이션이 설정되어 있을 경우, 타겟 메소드를 2번 호출하도록 합니다.
  • RunTwice 어노테이션 클래스
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface RunTwice{

}
  • AnnotationExam 클래스
import java.lang.reflect.Method;

public class AnnotationExam{
  //메소드1~3 중 원하는 부분에 @RunTwice 어노테이션을 붙여보세요.
  @RunTwice
  public void method1(){
    System.out.println("메소드 1 입니다.");
  }
  public void method2(){
    System.out.println("메소드 2 입니다.");
  }
  public void method3(){
    System.out.println("메소드 3 입니다.");
  }
    
  public static void main(String[] args){
    AnnotationExam exam = new AnnotationExam();
        
    try {
      Method method = exam.getClass().getDeclaredMethod("method1");
      if(method.isAnnotationPresent(RunTwice.class)){
        exam.method1();exam.method1();
      }
      method = exam.getClass().getDeclaredMethod("method2");
      if(method.isAnnotationPresent(RunTwice.class)){
        exam.method2();exam.method2();
      }
      method = exam.getClass().getDeclaredMethod("method3");
      if(method.isAnnotationPresent(RunTwice.class)){
        exam.method3();exam.method3();
      }  
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}