오늘의하루

[Java] 표준, 메타 Annotation(애너테이션) 본문

JAVA

[Java] 표준, 메타 Annotation(애너테이션)

오늘의하루_master 2022. 9. 2. 12:29
반응형

Java에서 제공하는 애너테이션에는 표준 애너테이션과 메타 애너테이션이 있습니다.

애너테이션 종류

표준 애너테이션 설명
@Override 컴파일러에게 오버라이딩하는 메서드라는 것을 알린다.
@Deprecated 사용하지 않을 것을 권장하는 대상에 붙인다.
@SuppressWarnings 컴파일러의 특정 경고를 나타나지 않게 해준다.
@SafeVarargs 제네릭 타입의 가변인자에 사용한다.
@FunctionalInterface 함수형 인터페이스라는 것을 알린다.
@Native native메서드에서 참조되는 상수 앞에 붙인다.
메타 애너테이션 설명
@Target* 애너테이션이 적용가능한 대상을 지정한다.
@Documented* 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
@Inherited* 애너테이션이 자식 클래스에 상속되도록 한다.
@Retention* 애너테이션이 유지되는 범위를 지정하는데 사용
@Repeatable* 애너테이션을 반복해서 적용할 수 있게 한다.

표준 - @Override

오버 라이딩을 올바르게 작성했는지 컴파일러가 체크를 하며, 오타 같은 실수를 잡아주는 역할을 한다.

class Parent{
    void parentMethod(){}
}

class Child extends Parent{
    void PARENTMETHOD(){} // 오버라이딩하려 했으나 오타가 발생
}
  • 오버 라이딩할 때는 메서드 선언부 앞에 @Override를 붙이면 사용할 수 있다.
class Parent{
    void parentMethod(){}
}

class Child extends Parent{
    @Override
    void PARENTMETHOD(){} // Error!!!
    
    @Override
    void parentMethod(){} // OK
}

표준 - @Deprecated

앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.

class Test{
    @Deprecated
    int x;
    
    @Deprecated
    public int getTest(){}
}
  • @Deprecated가 붙은 대상이 사용된 코드를 컴파일하면 나타나는 메시지이다.
    • Note : Annotation파일명 uses or overrides a deprecated API.
    • Note : Recompile with -Xlint:deprecation for details.

표준 - @FunctionalInterface

함수형 인터페이스에 붙이면 컴파일러가 올바르게 작성했는지 체크해준다.

  • 함수형 인터페이스는 하나의 추상 메서드만 가져야 한다는 제약이 있다.
@FuntionalInterface
interface Test{
    abstract void getTest(); // 추상메서드
}

@FuntionalInterface
interface Test{ // Error! 함수형 인터페이스는 추상메서드가 1개만 있어야한다.
    abstract void getTest(); // 추상메서드
    abstract void setTest(); // 추상메서드
}

표준 - @SuppressWarnings

컴파일러의 경고 메시지가 나타나지 않게 억제한다.

사용방법

  • 괄호() 안에 억제하고자 하는 경고의 종류를 문자열로 지정한다.
@SuppressWarnings("unchecked") // 제네릭스와 관련된 경고를 억제
ArrayList list = new ArrayList(); // 제네릭 타입을 지정하지 않았음
list.add(obj); // 경고 발생
  • 두 개 이상의 경고를 동시에 억제할 때는 배열처럼 작성한다.
@SuppressWarnings({"deprecation", "unchecked", "varargs"})
  • "-Xlint"옵션으로 컴파일하면 경고 메시지를 볼 수 있다.

메타 - @Target

애너테이션을 만들기 위한 애너테이션이며 애너테이션을 적용할 수 있는 대상을 지정한다.

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

메타 - @Retention

애너테이션이 유지되는 기간을 지정하는 데 사용한다.

유지정책 의미
SOURCE 소스 파일에만 존재, 클래스파일에는 존재하지 않는다.
CLASS 클래스 파일에 존재, 실행시에 사용불가 (기본값)
RUNTIME 클래스 파일에 존재, 실행시에 사용가능

컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE이다

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{}

실행 시에 사용 가능한 애너테이션의 정책은 RUNTIME이다.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FuntionalInterface{}

메타 - @Documented, @Inherited

javadoc으로 작성한 문서에 포함시키려면 @Documented를 붙인다.

@Document
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}

애너테이션을 자식 클래스에 상속하고자 할 때 @Inherited를 붙인다.

@Inherited // @SupperAnno가 자식까지 영향을 미친다.
@interface SupperAnno{}

@SuperAnno
class Parent{}

class Child extends Parent{} // Child에 SupperAnno애너테이션이 붙은 것으로 인식

메타 - @Repeatable

반복해서 붙일 수 있는 애너테이션을 정의하는 데 사용한다.

@Repeatable(ToDos.class) // ToDo 애너테이션을 여러번 사용할 수 있다.
@interface ToDo{ 
    String value();
}

사용하는 방법

@ToDo("delete test codes.")
@ToDo("override inherited methods")
@ToDo("Message write~")
class Myclass{}

애너테이션 타입 정의하기

애너테이션을 직접 만들어서 사용할 수 있다.

@interface 애너테이션 이름{
    타입 요소이름(); // 애너테이션 요소를 선언한다.
}

애너테이션의 메서드는 추상 메서드이며, 애너테이션을 적용할 때 모두 지정해야 한다. (순서 상관없음)

enum TestType {FIRST, FIANL}

@interface TestInfo{
    int count();
    String testBy();
    String[] testTools();
    TestType testType();
    DateTime testDate(); // 자신이 아닌 다른 애너테이션(@DateTime)을 포함할 수 있다.
}

위에서 만든 애너테이션을 사용해보기

@TestInfo(
    count = 3, testedBy = "Kim",
    testTools = {"JUnit", "AutoTester"},
    testType = TestType.FIRST,
    testDate = @DateTime(yymmdd = "160101", hhmmss = "235959")
)
public class NewClass{...}

애너테이션의 요소

  • 애너테이션을 적용 시 값을 지정하지 않았을 경우 사용할 수 있는 기본값을 지정 가능하다.
@interface TestInfo{
    int count() default 1; // 기본 값을 1로 지정한다.
}

위 애너테이션을 사용해보기

@TestInfo // @TestInfo(int count = 1)과 동일하다.
public class NewClass{...}
  • 요소가 하나이고 해당 요소의 이름이 value일 경우 생략이 가능하다.
@interface TestInfo{
    String value();
}

위 애너테이션을 사용해보기

@TestInfo("passed") // @TestInfo(value = "passed")와 동일하다.
class NewClass{...}
  • 요소의 타입이 배열인 경우 괄호{ }를 사용해야 한다.
@interface TestInfo{
    String[] testTools();
}

위 애너테이션을 사용해보기

@TestInfo(
    testTools = {"JUnit", "AutoTester"} // 배열이 2개 이상일 경우
)

@TestInfo(
    testTools = "JUnit" // 배열이 1개일 경우
)

@TestInfo(
    testTools = {} // 배열의 값이 없어도 괄호{}를 반드시 해줘야한다.
)

모든 애너테이션의 조상 Annotation

Annotation은 모든 애너테이션의 조상이지만 상속은 불가능하다.

@interface TestInfo extends Annotation{ // Error! 허용되지 않는 표현
    int count();
}

마커 애너테이션

요소가 하나도 정의되지 않은 애너테이션을 의미한다.

  • 예시로는 @Override, @Deprecated 등등

애너 테이션 요소의 규칙

애너테이션의 요소를 선언할 때 아래의 규칙을 반드시 지켜야 한다.

  1. 요소의 타입은 기본형, String, enum, 애너테이션, Class(설계도)만 허용된다.
  2. 괄호( ) 안에 매개변수를 선언할 수 없다.
  3. 예외를 선언할 수 없다.
  4. 요소를 타입 매개변수로 정의할 수 없다.
// Quiz

@interface AnnoTest{
    int id = 100; // OK 상수
    String major(int i, int j); // Error! 매개변수 사용 불가
    String minor() throws Exception; // 예외 선언 불가
    ArrayList<T> list(); // 타입 매개변수 정의 불가
}

예제를 통해 복습하기

import java.lang.annotation.*;

@Deprecated // 사용 중지 권장
@SuppressWarnings("1111") // 유효하지 않은 애너테이션이고 이런 애너테이션은 무시된다.
@TestInfo(
    testedBy = "aaaa",
    testDate = @DateTime(yymmdd = "160101", hhmmss = "235959")
    // 나머지도 적어줘야하지만 default 값이 있기 때문에 생략이 가능하다.
)
public class Myclass{
    public static void main(String[] args){
        Class<Myclass> cls = Myclass.class;
        // Myclass의 Class객체를 얻는다.
        
        TestInfo anno = cls.getAnnotation(TestInfo.class);
        System.out.println("anno.testedBy() = " + anno.testedBy()); // aaaa
        System.out.println("anno.testDate().yymmdd() = " + anno.testDate().yymmdd());
        // 160101
        System.out.println("anno.testDate().hhmmss() = " + anno.testDate().hhmmss());
        // 235959
        
        for(String str : anno.testTools()){
            System.out.println("testTools = " + str); // JUnit
        }
        System.out.println();
        
        Annotation[] annoArr = cls.getAnnotations();
        
        for (Annotation a : cls.getAnnotations()){
            System.out.println(a);
        }
        // @java.lang.Deprecated(forRemoval=false, since="")
        // @TestInfo(count=1, testType=First, testTools={"JUnit"},
                     testedBy="aaaa", testDate=@DateTime(yymmdd="160101",
                     hhmmss="235959"))
    }
}

enum TestType{First, Final}

@Retention(RetentionPolicy.RUNTIME) // 실행 시에 사용 가능하도록 지정
@interface TestInfo{
    int count() default 1; // 기본값 지정
    String testedBy();
    String[] testTools() default "JUnit"; // 기본값 지정
    TestType testType() default TestType.First; // 기본값 지정
    DateTime testDate();
}

@Retention(RetentionPolicy.RUNTIME) // 실행 시에 사용 가능하도록 지정
@interface DateTime{
    String yymmdd();
    String hhmmss();
}
반응형
Comments