[Java] Annotation 개념 및 정리

MINK·2023년 2월 21일
0

글을 쓰기 전 제가 생각하는 annotation은 코드를 조금 더 쉽게 쓸 수 있게 사용되는 것이며 프로젝트 당시 데이터를 호출하거나 api통신을 할때 편의를 위해서 사용된 내용?으로 생각했습니다. 하지만 자세하게 내용을 알아야 면접 또는 프로젝트를 진행할 때 내가 배웠던 내용을 토대로 설명을 할 수 있기때문에 리펙토링 기간동안 복습하게 되었습니다.

Annotation이란?

Annotation은 클래스와 메서드에 추가하며 다양한 기능을 부여하는 역할을 합니다.

Annotation을 활용하여 Spring Framework는 해당 클래스가 어떤 역할인지 정하기도 하고, Bean을 주입하기도 하며, 자동으로 getter나 setter를 생성하기도 합니다. 특별한 의미를 부여하거나 기능을 부여하는 등 다양한 역할을 수행할 수 있습니다.

이러한 Annotation을 통하여 코드량이 감소하고 유지보수하기 쉬우며, 생산성이 증가됩니다.

Annotation 특징

  • 컴파일러에게 필요한 정보를 제공
    • 컴파일러가 에러를 감지하거나, 경고를 띄우지않게 하기 위함.
  • 컴파일/배포 시에 필요한 처리 기능
    • SW 개발 툴에서 어노테이션의 정보를 통해 특정 코드를 추가할 수 있음.
  • 런타임 처리 제공
    • 런타임에도 어노테이션의 정보를 통해 필요한 처리를 할 수 있음.

이렇게 설명만 보면 어려움이 있어서 이 블로그를 보면서 이해함.

어노테이션 그림 설명

위 블로그 내용처럼 나도 그림을 그리면서 이해함.

과거의 파일 관리에대한 두 가지 어려움

  1. 사람들이 자바 코드는 변경하는데 설정 파일을 업데이트 하지 않는 어려움

  2. 설정과 코드가 분리되어있어, 개발에 대한 어려움

이 두 가지 어려움을 해결하고자 다음 관리방법을 설정

어노테이션의 종류

  • 표준(내장) 어노테이션 : 자바가 기본적으로 제공해주는 어노테이션
  • 메타 어노테이션 : 어노테이션을 위한 어노테이션? -> 어노테이션을 정의할 때 사용
  • 사용자정의 어노테이션 : 사용자가 직접 정의하는 어노테이션

표준(내장) 어노테이션

  1. @Override
  2. @Deprecated
  3. @SuppressWarnings

@Override

  • 오버라이딩을 올바르게 했는지 컴파일러가 체크
class Parent{
    void parentMethod(){}
}

class Child extends Parent{
    @Override
    void parentmethod(){}
    ..
}

@Deprecated

  • 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
  • "getDate()" <- 자바에서 메소드를 사용했는데 다음과 같이 표시된 경험
  • 이유는 해당 메소드 상위에 @Deprecated 어노테이션이 붙었기 때문
@Deprecated
public int getDate(){
    return normalize().getDayOfMonth();
}

@FunctionalInterface

  • 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크
  • 함수형 인터페이스의 "하나의 추상메서드만 가져야 한다는 제약"을 확인

@SuppressWarnings

  • 컴파일러의 경고메세지가 나타나지 않게 한다.
  • 보통 경고가 많을 때, 확인된 경고는 해당 어노테이션을 붙여서 새로운 경고를 알아보지 못하는 것을 방지하기 위해 사용한다.
// Array를 선언할 때 제네릭을 통해서 타입에 대한 정보를 기입하지않음.
// 타입을 선언하지 않았다는 "unchecked"라는 경고가 뜸.
// 하지만 @SuppressWarnings("unchecked")를 입력해주었기때문에 "unchecked"에 대한 경고는 억제됨.

@SuppressWarnings("unchecked")
ArrayList list = new ArrayList();
list.add(obj);

02.21 study내용


글쓴이 생각 : 표준 어노테이션에서 프로젝트를 진행하면서 봐왔던 내용은 @Override가 대표적인 것 같다.

@Override

  • 부모 클래스의 메서드명을 하위 클래스에서 잘못되는 것을 방지하면 컴파일 에러를 통해 체크할 수 있는 좋은 방법이라고 생각한다.

@Deprecated, @FunctionalInterface, @SuppressWarnings

  • 이번에 처음보게되 annotation들이지만 각각의 에러들을 조금 더 쉽게 보여주기 위함에서 쓰이는 것을 알게됌

2. 메타 어노테이션

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited
  5. @Repeatable

@Target

  • 어노테이션을 정의할 때, 적용대상을 지정하는데 사용함.

  • Java compiler가 annotation이 어디에 적용될 지 결정하기 위해 사용

  • 종류

    • ElementType.PACKAGE : 패키지 선언
    • ElementType.TYPE : 타입 선언
    • ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언
    • ElementType.CONSTRUCTOR : 생성자 선언
    • ElementType.FIELD : 멤버 변수 선언
    • ElementType.LOCAL_VARIABLE : 지역 변수 선언
    • ElementType.METHOD : 메서드 선언
    • ElementType.PARAMETER : 전달인자 선언
    • ElementType.TYPE_PARAMETER : 전달인자 타입 선언
    • ElementType.TYPE_USE : 타입 선언
    @Target({TYPE, FIELD, TYPE_USE})
    @Retention(RetentionPolicy.SOURCE())
    @public @interface MyAnnotation{}
    
    @MyAnnotation 
    class MyClass{
    	@MyAnnotation
        int i;
        
        @MyAnnotation
        MyClass mc:
        
    }

@Retention

  • 어노테이션의 유지되는 기간을 지정하는데 사용
  • Policy에 관련된 Annotation으로 컴파일 이후에도 JVM에서 참조가 가능한 RUNTIME으로 지정
  • 종류
    • RetentionPolicy.RUNTIME
      • 컴파일 이후에도 JVM에 의해서 계속 참조가 가능
      • 주로 Reflection이나 Logging에 사용
    • RetentionPolicy.CLASS
      • 컴파일러가 클래스를 참조할 때까지 유효
    • RetentionPolicy.SOURCE
      • 컴파일 전까지만 유효. 즉, 컴파일 이후에는 사라짐.
// SOURCE : 소스 파일에만 존재
// RUNTIME : 클래스 파일에 존재. 실행시에 사용가능
// CLASS : 클래스 파일에 존재. 실행 시에 사용 불가. 기본 값
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{}

@Documented

  • 애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 함.

@Inherited

  • 어노테이션도 상속이 가능하다.

  • 어노테이션을 자손 클래스에 상속하고자 할 때, @Inherited를 붙임

    @Inherited
    @interface SuperAnno{}
    
    @SuperAnno
    class Parent{}
    
    class Child extends Parent{}

@Repeatable

  • 반복해서 붙일 수 있는 어노테이션을 정의할 때 사용

  • @Repeatable이 붙은 애너테이션은 반복해서 붙일 수 있음.

    @Repeatable(ToDos.class)
    @interface ToDo{
    	String value();
    }
    
    @ToDo("delete test codes.")
    @ToDo("override inherited methods")
    class MyClass{
    	~~
    }
    
    @interface ToDos{
    	ToDo[] value();
    }

3. 사용자정의 어노테이션

  1. @interface로 어노테이션 클래스를 선언
  2. @Retention을 통해 어느 시점에 적용할지를 설정
  3. @Target을 통해 어떤 곳에 적용할지를 설정(클래스, 메서드, 변수 등등)
  4. 어노테이션은 내부적으로 값을 가질 수 있고, 여기에 값을 주면 옵션으로 값을 설정
    • 타입 + 이름 + () + default형태로 선언

어노테이션 사용하기

@Retry(maxRetryCount = 10, maxAbc = 111)
public String save(String itemId) {
	seq ++;
	if (seq % 2 == 0){
		throw new IllegalStateException("예외 발생");
	}
	return "ok";
}
  • 다음과 같이 만난 어노테이션을 적용
  • 어노테이션 내부에 설정했던 값은 옵션 값을 넣을 때 사용할 수 있게 된다.

Annotation 생성하기

  • 기본적인 문장
@interface 이름{
	타입 요소 이름(); // 어노테이션의 요소를 선언
	..
}
  • 적용
@interface DateTime{
	String yymmdd();
	String hhmmss();
}

@interface TestInfo{
    int count() default 1;
    String testedBy();
    TestType testType();
    DateTime testDate();
}

@TestInfo{
    testedBy = "Kim",
    testTools = {"JUnit", "AutoTester"},
    testType = TestType.FIRST,
    testDate = @DateTIme(yymmdd="210922", hhmmss="211311")
} // count를 생략했으므로 default인 "count=1"이 적용
public class NewClass{...}

어노테이션 요소 특징

  • 적용시 값을 지정하지 않으면, 사용될 수 있는 기본값을 지정
  • 요소가 하나이고 이름이 valuee일 때는 요소의 이름 생략가능
@interface TestInfo{
	String value();
}
@TestInfo("passed") //value="passed"와 동일
class NewClass{...}
  • 요소의 타입이 배열인 경우, 괄호{}를 사용
@interface TestInfo{
	String[] testTools();
}

@TestInfo(testTools={"JUnit", "AutoTester"})
@TestInfo(testTools="JUnit")
@TestInfo(testTool={})

어노테이션의 규칙

  • 요소의 타입은 기본형, String, enum, 어노테이션, Class만 허용
  • 괄호()안에 매개변수를 선언할 수 없음
  • 예외를 선언할 수 없음
  • 요소의 타입을 매개변수로 정의할 수 없음.()
// 잘못된 예시
@interface AnnoConfigTest{
	int id = 100; // 상수 ok
	String major(int i, int j) // 매개변수 x
	String minor() throws Exception; // 예외 x
	ArrayList<T> list(); // 요소의 타입을 매개변수 x
}
profile
parkminkyu velog

0개의 댓글