티스토리 뷰

Java의 정석_기초편

애너테이션

DJDU 2022. 11. 9. 15:49

애너테이션

  • 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공
  • 애너테이션의 사용예
더보기

주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공

애너테이션의 사용예

@Test - JUnit 단위 테스트 프로그램에 유용한 정보 제공하기 위한 것

@Test  // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
    ...
}

표준 애너테이션

  • Java에서 제공하는 애너테이션
  • 메타애너테이션 : 애너테이션을 만들 때 사용
더보기

Java에서 제공하는 애너테이션

  • 메타애너테이션 : 애너테이션을 만들 때 사용ㅈ
▲ 표12-2 자바에서 기본적으로 제공하는 표준 애너테이션(*가 붙은 것은 메타 애너테이션)

@Override

  • 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.
  • 오버라이딩할 때 메서드이름을 잘못적는 실수를 하는 경우가 많다.
  • 오버라이딩할 때는 메서드 선언부 앞에 @Override를 붙이자.
더보기

오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.

오버라이딩할 때 메서드이름을 잘못적는 실수를 하는 경우가 많다.

오버라이딩할 때는 메서드 선언부 앞에 @Override를 붙이자.

예제12-7

더보기

@Override를 주석처리하면, 에러가 잡히지 않는다.

class Parent {
    void parentMethod() { }
}

class Child extends Parent {
    // @Override
    void parentmethod() { } // 조상 메서드의 이름을 잘못 적었음.
}

Problem Description | Method does not override method from its superclass

해석 : 이 메서드(parentmethod)는 조상의 메서드를 오버라이딩하지 않았다.

@Deprecated

  • 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
  • @Deprecated의 사용 예, Date클래스의 getDate()
  • @Deprecated가 붙은 대상이 사용된 코드를 컴파일하면 나타나는 메시지
더보기

@Deprecated의 사용 예, Date클래스의 getDate()

@Deprecated
public int getDate() {
    return normalize().getDayOfMonth();
}

삭제하지 않고 이렇게 처리하는 이유는 Java가 '하위 호환성'을 고려하기 때문이다.

Java API 문서

@Deprecated가 붙은 대상이 사용된 코드를 컴파일하면 나타나는 메시지

  • @Deprecated된 필드나 메서는 에러가 아니다.

예제12-7-2

더보기

IDE 환경에서는 자동으로 컴파일을 해주기 때문에, 아까 같은 메시지가 있는지 없는지 모른다. 따라서 터미널 환경에서 직접 파일을 실행해보자.

 jiwoo@Jiwooui-MacBookPro  ~/java_basic/java_basic/src/ch12   main ±✚  javac Ex12_7_2.java
Ex12_7_2.java
Note: Ex12_7_2.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors

위에서 배웠던 메세지가 그대로 나왔다. -Xlint:deprecation이라는 옵션을 주고 다시 컴파일해보자(javac).

~/java_basic/java_basic/src/ch12  javac -Xlint:deprecation Ex12_7_2.java
Ex12_7_2.java:16: warning: [deprecation] parentMethod() in Child2 has been deprecated
        c.parentMethod();  // deprecated된 메서드 사용
         ^
1 warning

경고(warning)에 대한 세부내용이 출력된다.

@FunctionalInterface

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

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

함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있음

  • 안 붙여도 에러가 나지는 않는다.
  • 함수 인터페이스라는 것을 알려주는 효과도 있다.
  • 추상 메서드가 하나 있다.(run())
@FunctionalInterface
public interface Runnable {
    public abstract void run();  // 추상 메서드
}

| 참고 | '함수형 인터페이스'는 14장에서 배운다.

예제12-7-2

더보기

Warning | Multiple non-overriding abstract methods found in interface ch12.Testable

해석 : 두 개 이상의 오버라이딩되지 않은 추상 메서드가 Testable인터페이스에서 발견되었다.

@FunctionalInterface
interface Testable {  // 함수형 인터페이스는 하나의 추상메서드만 가능
    void test();  // 추상메서드
    void check(); // 추상메서드
}

함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있기 때문에, 이러한 경고 메세지가 컴파일러에 의해 출력된다.

@SuppressWarnings

  • 컴파일러경고메시지가 나타나지 않게 억제한다.
  • 괄호()안에 억제하고자 하는 경고의 종류를 문자열로 지정.
  • 둘 이상의 경고를 동시에 억제하려면 다음과 같이 한다.
  • '-Xlint'옵션으로 컴파일하면, 경고메시지를 확인할 수 있다. 괄호[] 안의 경고의 종류. 아래의 경우 rawtypes
더보기

괄호()안에 억제하고자 하는 경고의 종류를 문자열로 지정

@SuppressWarnings("unchecked").    // 지네릭스와 관련된 경고를 억제
ArrayList list = new ArrayList();  // 지네릭 타입을 지정하지 않았음.
list.add(obj);                     // 여기에서 경고가 발생

@SuppressWarnings 애너테이션을 붙인 것은 컴파일러가 알려준 경고를 확인했다는 표시이다.

둘 이상의 경고를 동시에 억제하려면 다음과 같이 한다.

@SuppressWarnings({"deprecation", "unchecked", "varargs"});

'-Xlint'옵션으로 컴파일하면, 경고메시지를 확인할 수 있다. 괄호[] 안의 경고의 종류. 아래의 경우 rawtypes

~/java_basic/java_basic/src/ch12  javac -Xlint:deprecation Ex12_7_2.java
Ex12_7_2.java:16: warning: [deprecation] parentMethod() in Child2 has been deprecated
        c.parentMethod();  // deprecated된 메서드 사용
         ^
1 warning

예제12-7-2

더보기
~/java_basic/java_basic/src/ch12  javac -Xlint:deprecation Ex12_7_2.java
Ex12_7_2.java:16: warning: [deprecation] parentMethod() in Child2 has been deprecated
        c.parentMethod();  // deprecated된 메서드 사용
         ^
1 warning

위 처럼 메세지가 안 나오게 하려면 호출하는 메서드(main메서드)에 @SuppressWarnings("deprecation") 애너테이션을 사용하면 된다.

IDE가 아닌 터미널 환경에서 다시 소스파일을 컴파일해보자.

~/java_basic/java_basic/src/ch12  javac -Xlint:deprecation Ex12_7_2.java

메세지가 나오지 않는 것을 확인할 수 있다.

~/java_basic/java_basic/src/ch12  javac -Xlint:deprecation Ex12_7_2.java


메타 애너테이션

  • 메타 애너테이션은 '애너테이션을 위한 애너테이션'
  • java.lang.annotation패키지에 포함
더보기

메타 애너테이션은 '애너테이션을 위한 애너테이션'

java.lang.annotation패키지에 포함

@Target

  • 애너테이션을 정의할 때, 적용대상 지정에 사용
더보기

애너테이션을 정의할 때, 적용대상 지정에 사용

@Retention

  • 애너테이션이 유지(retention)되는 기간을 지정하는데 사용
  • 컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE이다.
  • 실행시에 사용 가능한 애너테이션의 정책은 RUNTIME이다.
더보기

애너테이션이 유지(retention)되는 기간을 지정하는데 사용

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

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

(참고)@Documented

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

(참고)@Inherited

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

(참고)@Repeatable

  • 반복해서 붙일 수 있는 애너테이션을 정의할 때 사용
  • @Repeatable이 붙은 애너테이션은 반복해서 붙일 수 있다.
  • @Repeatable인 @ToDo를 하나로 묶을 컨테이너 애너테이션도 정의해야 함



애너테이션 타입 정의하기

  • 애너테이션을 직접 만들어 쓸 수 있다.
  • 애너테이션의 메서드는 추상 메서드이며, 애너테이션을 적용할 때 지정(순서X)
더보기

애너테이션을 직접 만들어 쓸 수 있다.

@interface 애너테이션이름 {
    타입 요소이름();  // 애너테이션의 요소를 선언한다.
}
@interface DateTime {
    String yymmdd();  // 날짜
    String hhmmss();  // 시간
}

DateTime이라는 애너테이션은 두 개의 요소를 가지고 있다. 두 요소 모두 '추상메서드'이다.

애너테이션의 메서드는 추상 메서드이며, 애너테이션을 적용할 때 지정(순서X)

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

TestInfo애너테이션을 만들었다. 이제 TestInfo애너테이션을 사용해보자.

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

anno.count() = 3;이 나온다. 우리는 호출보다는 사용하는 경우가 훨씬 많다.

애너테이션의 요소

  • 적용 시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null제외)
  • 요소가 하나이고 이름이 value일 때는 요소의 이름이 생략가능
  • 요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.
더보기

적용 시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null제외)

@interface TestInfo {
    int count() default 1;  // 기본값을 1로 지정
}
@TestInfo // @TestInfo(count=1)과 동일
public class NewClass { ... }

요소가 하나이고 이름이 value일 때는 요소의 이름이 생략가능

@interface TestInfo {
    String value();
}
@TestInfo("passes")    // @TestInfo(value="passed")와 동일
class NewClass { ... }

| 참고 | 요소의 이름이 별루(value)일 떄는 생략 가능하다고 생각하시면 외우기 쉽습니다.

요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.

@interface TestInfo {
    String[] testTools();
}
@Test(testTools={"JUnit", "AutoTester"})
@Test(testTools="JUnit")
@Test(testTools={})  // 값이 없을 때는 괄호{}가 반드시 필요

모든 애너테이션의 조상 - java.lang.annotation.Annotation

  • Annotation은 모든 애너테이션의 조상이지만 상속은 불가
  • 사실 Annotation은 인터페이스이다.
더보기

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

@interface TestInfo extends Annotation {  // 에러. 허용되지 않는 표현
    int       count();
    String testedBy();
        ...
}

사실 Annotation은 인터페이스이다.

  • 추상메서드 구현하지 않고, 사용 가능
package java.lang.annotation;

public interface Annotation {  // Annotation자신은 인터페이스이다.
    boolean equals(Object obj);
    int    hashCode();  // 추상메서드 - 구현x but 사용 가능
    String toString();  // 추상메서드 - 구현x but 사용 가능
    
    Class<? extends Annotation> annotationType();  // 애너테이션의 타입을 반환
}

마커 애너테이션 - Marker Annotation

  • 요소가 하나도 정의되지 않은 애너테이션
더보기

요소가 하나도 정의되지 않은 애너테이션

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}     // 마커 애너테이션. 정의된 요소가 하나도 없다.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Test {}         // 마커 애너테이션. 정의된 요소가 하나도 없다.
@Test  // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
        ...
}

@Deprecated
public int getDate() {
    return normalize().getDayOfMonth();
}

애너테이션 요소의 규칙

  • 애너테이션의 요소를 선언할 때 아래의 규칙을 반드시 지켜야 한다.
  • 아래의 코드에서 잘못된 부분은 무엇인지 생각해보자.
더보기

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

  • 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용됨
  • 괄호()안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다.
  • 요소를 타입 매개변수로 정의할 수 없다.

아래의 코드에서 잘못된 부분은 무엇인지 생각해보자.

@interface AnnoTest {
    int id = 100;
    String major(int i, int j);
    String minor() throws Exception;
    ArrayList<T> list();
}

int id = 100;

상수 OK(static final 생략), default메서드 X

String major(int i, int j);

에러. 괄호()안에 매개변수를 선언할 수 없다.

String minor() throws Exception;

에러. 예외를 선언할 수 없다.

ArrayList<T> list();

에러. 요소를 타입 매개변수로 정의할 수 없다.

예제12-8

'Java의 정석_기초편' 카테고리의 다른 글

쓰레드(thread)  (0) 2022.11.10
열거형  (0) 2022.11.08
지네릭 타입의 형 변환  (0) 2022.11.08
와일드카드 & 지네릭 메서드  (0) 2022.11.08
제한된 지네릭 클래스 & 지네릭스의 제약  (0) 2022.11.08
댓글
공지사항