ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 표준, 메타 Annotation(애너테이션)
    JAVA 2022. 9. 2. 12:29
    728x90
    반응형

    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();
    }
    728x90
    반응형
Designed by Tistory.