티스토리 뷰

애노테이션

  • 프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것
  • 주석과 마찬가지로 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보 제공

표준 애노테이션

  • JDK에서 제공
  • 주로 컴파일러를 위한 것

@Override

  • 메서드 앞에만 붙일 수 있음
  • 조상 클래스의 메소드를 오버라이딩한다는 것을 컴파일러에게 알려줌
  • 오버라이딩 할 때 메소드 이름을 잘못 적는 휴먼폴트를 방지할 수 있음

@Deprecated

  • 더 이상 사용되지 않는 필드나 메서드에 붙임
  • 다른 것으로 대체되었으니 사용하지 않을 것을 권함
  • 컴파일시 deprecated된 대상을 사용하고 있다고 에러메시지 띄움

@FunctionalInterface

  • 함수형 인터페이스를 올바르게 선언했는지 즉, 추상메서드가 하나뿐인지 확인하고 잘못된 경우 컴파일 에러를 발생시킴

@SuppressWarnings()

  • 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제
  • “deprecation”
  • “unchecked”
    • 지네릭스로 타입을 지정하지 않았을 때 발생하는 경고 억제
  • “rawtypes”
    • 지네릭스를 사용하지 않아서 발생하는 경고 억제
  • “varargs”
    • 가변인자 타입이 지네릭스 타입일 때 발생하는 경고 억제
@SuppressWarnings("unchecked")
ArrayList list = new ArrayList();
list.add(obj);

@SafeVarargs

  • reifiable 타입 : 컴파일 후에도 제거되지 않는 타입
  • non-reifiable 타입 : 컴파일 후 제거되는 타입
  • 가변 인자의 타입이 non-reifiable 타입일 경우 해당 메서드 선언부와 호출부에서 unchecked 경고가 발생. 이를 억제할 때 @SafeVarargs 를 붙인다.
  • static 이나 final이 붙은 메서드, 생성자에서만 사용 가능. 즉, 오버라이드될 수 있는 메서드에서는 사용 불가
class MyArrayList<T> {
    T[] arr;

    public MyArrayList(T[] arr) {
        this.arr = arr;
    }

    @SafeVarargs    //unchecked 경고 억제
    @SuppressWarnings("varargs")    //varargs 경고 억제
    public static <T> MyArrayList<T> asList(T... a) {
        return new MyArrayList<>(a);
    }

    @Override
    public String toString() {
        return Arrays.toString(arr);
    }
}

public class AnnotationEx4 {
    public static void main(String[] args) {
        MyArrayList<String> list = MyArrayList.asList("1", "2", "3");
        System.out.println(list);
    }

}

애노테이션 정의하는 방법

@interface 애너테이션이름 {
    타입 요소이름();
}
  • 애너테이션의 요소(element) : 애노테이션 내에 선언된 메서드

    • 요소 타입으로는 기본형, String, enum, 애노테이션, 클래스만 허용
    • 매개변수 선언불가
    • 예외 선언불가
    • 타입 매개변수로 정의 불가
    • 애노테이션을 적용할 때 요소들의 값을 빠짐없이 지정해주어야 함
      • 요소의 이름이 value이고 값이 하나뿐일 경우 적용할 때 이름을 생략할 수 있음
  • 모든 애노테이션의 조상은 Annotation (인터페이스) 이다.

    • 어노테이션 객체에 대해 equals(), hashCode(), toString() 과 같은 메서드 호출 가능

@Deprecated
@SuppressWarnings("1111")
@TestInfo(testedBy = "aaa", testDate = @DateTime(yymmdd = "220225", hhmmss = "090300"))
public class AnnotationEx5 {

    public static void main(String[] args) {
        Class<AnnotationEx5> cls = AnnotationEx5.class;

        TestInfo anno = cls.getAnnotation(TestInfo.class);
        System.out.println("anno.testedBy() = " + anno.testedBy());
        System.out.println("anno.testDate().yymmdd() = " + anno.testDate().yymmdd());

        for (String str : anno.testTools()) {
            System.out.println("testTools = " + str);
        }

        System.out.println();

        Annotation[] annoArr = cls.getAnnotations();
        for (Annotation annotation : annoArr) {
            System.out.println("annotation = " + annotation);
        }
    }
}

@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();
}

enum TestType {FIRST, FINAL}

메타 애노테이션

애노테이션을 정의하는데 사용되는 애노테이션

@retention

  • 애노테이션이 유지되는 기간 지정
    • SOURCE : 소스 파일에만 존재, 클래스파일(바이트코드)에는 존재하지 않음.
      • 컴파일러가 사용하는 애노테이션. e.g. Override, SuppressWarnings
    • CLASS : 클래스파일에 존재, 실행시에 사용 불가. 기본값
      • 클래스파일이 JVM에 로딩될 때 애너테이션 정보가 무시되어 실행 시 애너테이션에 대한 정보를 얻을 수 없음. 잘 사용되지 않음.
    • RUNTIME : 클래스 파일에 존재, 실행시에 사용 가능
      • e.g. FunctionalInterface : 컴파일러가 체크하지만, 실행 시에도 사용됨
      • 리플렉션이 가능해짐.

@target

  • 애노테이션이 적용가능한 대상 지정
    • ANNOTATION_TYPE
    • CONSTRUCTOR
    • FIELD : 필드 ( 멤버변수, enum상수). e.g. 기본형 변수
    • LOCAL_VARIABLE
    • METHOD
    • PACKAGE
    • PARAMETER
    • TYPE : 타입(클래스, 인터페이스, enum)
    • TYPE_PARAMETER
    • TYPE_USE : TYPE 변수 선언할 때. e.g. 참조형 변수
@Target({FIELD, TYPE, TYPE_USE})
public @interface MyAnnotation {
}

@MyAnnotation    //TYPE
class MyClass {
    @MyAnnotation    //FIELD
    int i;

    @MyAnnotation    //TYPE_USE
    MyClass mc;
}

@Documented

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

@Inherited

  • 애너테이션이 자손 클래스에 상속되도록 함

애노테이션 프로세서

  • 컴파일 타임에 애노테이션들을 스캐닝하고 프로세싱
  • javac에 속한 빌드툴
  • 자바코드를 인풋으로 받아서 아웃풋으로 파일(보통 .java)을 생성. 즉, 자바 코드를 생성할 수 있음.
    • 단, 이미 존재하는 자바 파일을 수정하는 행위는 불가
  • 모든 프로세서는 AbstractProcessor 를 상속 받음
    • void init(ProcessingEnvironment env)
    • boolean boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env)
      • 프로세서의 main() 메서드 역할.
      • scanning, evaluating, 어노테이션 프로세싱, 자바 파일 생성하는 코드 등 작성
    • Set getSupportedAnnotationTypes()
      • 어노테이션 프로세서가 처리할 어노테이션 명시
      • Full Qualified name을 set에 넣어서 반환
    • SourceVersion getSupportedSourceVersion()
      • 사용하는 자바버전 명시.
      • 보통, SourceVersion.latestSupported() 를 반환

기선님 리뷰 영상

  • 애너테이션은 일종의 주석과 같음.
  • 동적으로 바뀌는 값(변수)는 애너테이션에 넣을 수 없음.
    • e.g. static final(상수) 값만 애너테이션 엘리먼트 값으로 넣을 수 있음
  • 에러메시지 안 읽으면 개발자가 아니다.
  • source -> class -> runtime
  • 리플렉션 (더자바 코드조작 강의)
    • JVM 메모리에 있는 정보를 읽어옴
    • 클래스명.class.getDeclaredAnnotations() : @Inherited 된 어노테이션을 제외하고, 자식 클래스에 선언되어 있는 annotation 들을 얻어올 수 있음. (private 으로 선언되어 있는 것도 가져올 수 있음)
  • Retention

    • 커스텀한 어노테이션을 만든다면 Retention을 무엇으로 설정할지 고민해보아라.
      • 리플렉션까지 사용을 안할 것 같아. -> CLASS로 내려감
      • 소스코드에서만 쓸 것 같아. -> SOURCE로 한 단계 더 내려감
  • javadoc 활용 끝판왕 : mockito

  • ServiceLoader

    • Interface 만 알고 있는 상태에서 구현체를 사용할 수 있음. 즉, 구현체가 뭔지 모르는 상태에서 구현체를 사용할 수 있음.
    • 구현체 jar 파일을 생성하기 전에,
    • resources/META-INF/services 하위에 인터페이스의 Fully Qulified Name 으로 파일을 하나 생성
      • 해당 파일 내에 구현체의 FQN을 명시.
    • 구현체 Jar 파일을 만들고 나면, 이 Jar 파일을 사용하는 프로젝트에서는 구현체 정보를 모르고, 인터페이스 정보만 아는 상태에서 구현체를 사용할 수 있게 됨.
  • 개발자의 중요한 소양. 얼마나 빠르게 학습할 수 있는가.

    • 이를 위해서는 베이스를 쌓아 가야 함. 무엇이든 공부를 계속 해나가는 것이 중요.
  • 리플렉션과 어노테이션 프로세싱을 구분하자

    • 어노테이션 프로세싱 : 어노테이션 프로세서를 따로 구현을 해놓은 것.

References

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함