JAVA

[Java] Annotation

SangRok Jung 2022. 8. 4. 12:29
반응형

Annotation


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

프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨다.

 

 

 

 

▷ 예시

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

 

 

 

 

 

 

 

▷ 표준 애너테이션

자바에서 기본적으로 제공하는 표준 에너테이션

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

 

 

 

 

 

 

▷ @Override

  • 오버라이딩을 오바르게 했는지 컴파일러가 체크하게한다.
  • 오버라이딩 할 때 메서드 이름을 잘못 적는 실수를 하는 경우를 예방한다.
class Parent{
    void parentMethod();
}

class Child extends Parent{
    @Override
    void parentmethod(); // 메서드 이름을 잘못 적음
}

 

 

 

 

 

 

 

▷ @Deprecated

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

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

 

 

 

 

 

 

 

 

▷ @FunctionalInterface

  • 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크한다.
  • 함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있다.
@FuncionalInterface
public interface Runnable {
	public abstract void run(); //추상 메서드
}

 

 

 

 

 

 

 

 

▷ @SuppressWarnings

  • 컴파일러의 경고메세지가 나타나지 않게 억제한다.
  • 괄호 안에 억제하고자 하는 경고의 종류를 문자열로 지정한다.
@SuppressWarnings("unchecked")		// 지네릭스와 관련된 경고를 억제한다.
ArrayList list = new ArrayList();	// 지네릭 타입을 지정하지 않는다.
list.add(obj);						// 여기서 경고가 발생한다.


// 둘이상의 경고를 동시에 억제하려면 다음과 같이 한다.
@SuppressWarnings({"deprecation", "unchecked", "varargs"})

 

 

 

 

 

 

 

 

▷ 메타 에너테이션

  • 메타 에너테이션은 에너테이션을 위한 에너테이션이다.
  • java.lang.annotaion 패키지에 포함되어있다.

 

애너테이션 설명
@Target 애너테이션이 적용가능한 대상을 지정하는데 사용한다.
@Documented 애너테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
@Inherited 애너테이션이 자손 클래스에 상속되도록 한다.
@Retention 에너테이션이 유지되는 범위를 지정하는데 사용한다.
@Repeatable 애너테이션을 반복해서 적용할 수 있게한다. (JDK1.8)

 

 

 

 

 

 

 

 

▷ @Target

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

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
	String[] value();
}

 

 

@Target으로 지정할 수 있는 에너테이션의 종류

대상 타입 의미
ANNOTATION_TYPE 에너테이션
CONSTRUCTOR 생성자
FIELD 필드(맴버변수, enum상수)
LOCAL_VARIABLE 지역변수
METHOD 메서드
PACKAGE 패키지
PARAMETER 매개변수
TYPE 타입(클래스, 인터페이스, enum)
TYPE_PARAMETER 타입 매개변수(JDK 1.8)
TYPE_USE 타입이 사용되는 모든 곳(JDK 1.8)

 

 

 

 

 

 

 

 

 

▷ @Retetion

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

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

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

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

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

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

 

 

 

 

 

 

▷ @Documented, @Inherited

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

@Doucumentd
@Retedtion(RetentionPolicy.RUNTIME)
@Targer(ElementType.TYPE)
public @interface FuncinalInterface {}

 

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

@Inherited
@interface SuperAnno {}

@SuperAnno
class Parent {}

class Child extends Parent {}

 

 

 

 

 

 

 

▷ @Repeatable

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

@Repeatable(ToDos.class)
@interface ToDo {
	String value();
}

 

@Repeatble이 붙은 애너테이션은 반복해서 붙일 수 있다.

@ToDo("delete test codes.")
@ToDo("override inherited methods")
class MyClass{
	...
}

 

@Repeatable인 @ToDo를 하나로 묶을 컨테이너 에너테이션도 정의해야 한다.

@interface ToDos {
	ToDo[] value();
}

 

 

 

 

 

 

 

▷ 애너테이션 타입 정의하기

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

@interface 애너테이션 이름{
	타입 요소이름();
    ...
}

 

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

@interface TestInfo {
	int count();
    String testedBy();
    String[] testTools();
    TestType testType();
    DateTime testDate();
}

@interface DateTime{
	String yymmdd();
    String hhmmss();
}

 

 

 

 

 

 

 

▷ 애너테이션의 요소

애너테이션 내에 선언된 메서드를 '에너테이션의 요소' 라고 한다.

 

아래에 선언된 TestInfo애너테이션은 다섯개의 요소를 갖는다.

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

@interface DateTime{
    String yymmdd();    
    String hhmmss();
}

 

 

애너테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며, 상속을 통해 구현하지 않아도 된다.

다만, 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해주어야 한다.

@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(count=1)과 동일
public class NewClass {
    ...
}

 

 

 

애너테이션 요소가 오직 하나뿐이고 이름이 value인 경우, 애너테이션을 적용 할 때 요소의 이름을 생략하고 값만 적어도 된다.

@interface TestInfo{
    String value();
}

@TestINfo("passed")
class NewClass {
    ...
}

 

 

 

 

요소의 타입이 배열인 경우, 괄호{}를 사용해서 여러 개의 값을 지정 할 수 있다.

@Test(testTools = {"JUnit", "AutoTester"})  //값이 여러개인 경우
@Test(testTools = "JUnit")                  //값이 하나일 때는 괄호{} 생략 가능
@Test(testTools={})                         //값이 없을 때는 괄호{}가 반드시 필요

 

 

 

 

 

기본값을 지정 할 때도 마찬가지로 괄호{}를 사용할 수 있다.

@interface TestInfo{
    String[] info()     default {"aaa", "bbb"};     //기본값이 여러 개인 경우, 괄호{} 사용
    String[] info2()    default "ccc";                 //기본값이 하나인 경우, 괄호 생략 가능
}

@TestInfo               // @TestInfo(info = {"aaa", "bbb"}, info2 = "ccc") 와 동일
@TestInfo(info2={})     // @TestInfo(info = {"aaa", "bbb"}, info2 = {}) 와 동일
class NewClass { ... }

 

 

 

 

요소의 타입이 배열일 때도 요소의 이름이 value이면, 요소의 이름을 생략할 수 있다. 예를 들어, @SuppressWarnings의 경우, 요소의 타입이 String배열이고 이름이 value이다.

@interface SuppressWarnings {
    String[] value();
}

그래서 애너테이션을 적용 할 때 요소의 이름을 생략 할 수 있는 것이다.

//@SuppressWarnings (value={"deprecation", "unchecked"})
@SuppressWarnings ({"deprecation", "unchecked"})
class NewClass{
    ...
}

 

 

 

 

 

 

 

 

 

▷ 모든 애너테이션의 조상

모든 애너테이션의 조상은 Annotation이다. 

그러나 애너테이션은 상속이 허용되지 않으므로 아래와 같이 명시적으로 Annotation을 조상으로 지정할 수 없다.

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

 

 

 

 

Annotation은 아래와 같이 일반적인 인터페이스로 정의되어 있다.

package java.lang.annotation;

public interface Annotation{    // Annotation 자신은 인터페이스다.
    boolean equals(Object obj);
    int hashCode();
    String toString();

    Class<? extends Annotations> annotationType();  // 애너테이션의 타입을 반환
}

 

 

모든 애너테이션의 조상인 Annotation 인터페이스가 위와 같이 정의 되어 있기 때문에, 모든 애너테이션 객체에 대해 equals(), hashCode(), toString()과 같은 메서드를 호출하는 것이 가능하다.

 

 

 

 

 

 

 

▷ 마커 애너테이션

값을 지정할 필요가 없는 경우, 에너테이션의 요소를 하나도 정의하지 않을 수 있다.

Serializable이나 Cloneable 인터페이스 처럼 요소가 하나도 정의되지 않은 애너테이션을 마커 애너케이션이라 한다.

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

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

 

 

 

 

 

 

 

 

▷ 애너테이션 요소의 규칙

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

 

다음 코드에서 무엇이 잘못되었는지 찾아보자.

@interface AnnoTest{
    int id = 100;                       //OK, 상수 선언, static fianl int id = 100;
    String major(int i, int j);         //에러, 매개변수를 선언할 수 없음
    String major() throws Exception;    //에러, 예외를 선언할 수 없음
    ArrayList<T> list();                //에러, 요소의 타입에 매개변수 사용 불가
}

 

 

 

 

반응형

'JAVA' 카테고리의 다른 글

[Java] Coding Test (Pick two and add them)  (0) 2022.08.08
[Java] Coding Test (Lotto)  (0) 2022.08.04
[Java] enum(열거형)  (0) 2022.08.03
[Java] Generic Programming  (0) 2022.08.01
[Java] WildCard  (0) 2022.08.01