본문 바로가기
멀티캠퍼스 풀스택 과정/Java의 정석

자바의 정석9-4 애너테이션(annotation)

by 이쟝 2022. 1. 12.

애너테이션이란?

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

- 원래 소스코드와 문서가 따로 있었지만 관리하기 편하게 하기 위해서 소스코드 + 문서를 합침

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

 

- 애너테이션의 사용예

 

 

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

 

 

-> 메타애너테이션(@Target*, @Documented, @inherited, @Retention, @Repeatable): 애너테이션을 만들 때 사용하는 애너테이션

 


표준애너테이션

1. @Override

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

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

 

오버라이딩할 때 선언부 앞에 @override를 붙이자!

 

-> 오버라이딩 할 때 메서드 이름을 잘 못 적으면 오류가 남!

 

class Parent { 
	void parentMethod() {}
}

class Child extends Parent { 
	@Override
//	void parentmethod() {}  // 에러
	void parentMethod() {} 
}

 

2. @Deprecated

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

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

 

 

-> 자바는 하휘호환성을 중요하기 때문에 없애지는 않고, 사용을 권장하지 않음.

- @Deprecated가 붙은 대상이 사용된 코드를 컴파일 하면 경고가 나타남. 글자중간에 작대기

 

3. @FunctionalInterface

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

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

 

 

-> 함수형 인터페이스 안에 0개나 2개의 추상메서드가 있다면 오류!

-> @Override 처럼 사용 안해도 되지만, 실수오류를 제거할 수 있음

 

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

 

4. @SuppressWarnings

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

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

 

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

 

-> ArrayList는 지네릭클래스인데 이 코드에서 지네릭타입<T>을 지정하지 않아서 “unchecked” 경고발생

-> @SuppressWarnings을 사용하는 이유는 경고를 확인했다는 의미. 컴파일러가 알려주는 경고를 보고 애너테이션을 추가

 

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

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

 


메타 애너테이션

- ‘애너테이션을 위한 애너테이션’ -> 애너테이션을 만들 때 사용함

- 메타 애너테이션은 java.lang.annotiation 패키지에 포함

 

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

 

1. @Target

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

 

 

 

-> MyAnnotation이라는 애너테이션을 정의(public @interface MyAnnotation { })

-> 이 애너테이션 안에는 FIELD, TYPE, TYPE_USE만 사용 가능(@Target({FIELD, TYPE, TYPE_USE}))

-> FIELD: 필드(iv, cv ) , TYPE: 클래스, 인터페이스, TYPE_USE: 타입이 사용되는 모든 곳(클래스, 인터페이스)

 

2.@Retention

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

 

애너테이션의 유지 정책(retention policy)의 종류

 

-> SOURCERUNTIME을 주로 사용 

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

-> @Override 애너테이션은 컴파일러가 오버라이딩을 제대로 하는지 체크하기 때문에 SOURCE 사용(클래스파일에 굳이 존재하지 않아도 됨)

 

 

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

-> 유지 정책을 ‘RUNTIME’으로 하면, 실행 시에 리플랙션(reflection)’을 통해 클래스 파일에 저장된 애너테이션의 정보를 읽어서 처리할 수 있다.

-> @FunctionalInterface@Override처럼 컴파일러가 체크해주는 애너테이션이지만, 실행 시에도 사용되어서 유지정책이 RUNTIME이다.

 

3. @Documented, @Inherited

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

 

 

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

 

 

-> 두 애너테이션 모두 다 잘 사용되지는 않는다. 

 

4. @Repeatable

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

 

 

- @Repeatable이 붙은 애너테이션은 반복해서 붙일 수 있다.(하나의 대상에 애너테이션을 여러 번 붙일 수 있다. )

 

 

- @Repeatable@ToDo를 하나로 묶을 컨테이너 애너테이션도 정의해야 함

 

 


애너테이션 타입 정의하기

애너테이션 만드는 방법

 

 

@interface DateTime {
     String yymmdd( ); // 날짜
     String hhmmss( ); // 시간
}

 

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

 

애너테이션 TestInfo 정의

 

애너테이션 TestInfo 사용


애너테이션의 요소

 

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

 

 

- 요소의 한 개의 이름이 value일 때만 생략 가능함.

 

 

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

 

-> 배열일 때 요소가 한 개이면 { }가 생략가능 하지만, 여러 개 일 경우  { } 필요

-> default는 값은 { } ( 빈 괄호 { } )


모든 애너테이션의 조상

- java.lang.annotation.Annotation

- Annotation은 모든 애너테이션의 조상이지만 상속은 불가, Annotation은 인터페이스이기 때문

 

 

-> Annotation에 있는 추상메서드를 구현하지 않고도 사용 가능!(컴파일러가 자동으로 추상메서드 구현)

 

마커 애너테이션 - Marker Annotation

- 요소가 하나도 정의되지 않은 애너테이션(값을 지정할 필요가 없다.)

 

 


애너테이션 요소의 규칙

-> 요소의 타입은 기본형, String, enum, 애너테이션, Class(설계도 객체)만 허용됨

-> 괄호( )안에 매개변수를 선언할 수 없다. (추상메서드)

-> 예외를 선언할 수 없다.

-> 요소를 타입 매개변수로 정의할 수 없다. <T>

 

@interface AnnoTest {

       int id = 100;                             // OK 상수선언,  static final int id = 100;

       String major(int i, int j);               // 에러. 매개변수를 선언할 수 없음

       String minor( ) throws Exception;  // 에러. 예외선언을 할 수 없음

       ArrayList<T> list ( );                   // 에러. 요소의 타입에 타입 매개변수 사용 불가

}

 

예제)

 

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)  // 실행 시에 사용가능하도록 지정
@interface TestInfo {  // TestInfo 애너테이션 생성
	int       count()       default 1;
	String    testedBy();
	String[]  testTools()   default "JUnit";
	TestType  testType()    default TestType.FIRST;
	DateTime  testDate();
}

@Retention(RetentionPolicy.RUNTIME)  // 실행 시에 사용가능하도록 지정
@interface DateTime {   // DateTime 애너테이션 생성
	String yymmdd();
	String hhmmss();
}

enum TestType { FIRST, FINAL } // 열거형

@Deprecated  // 사용 권장 안함
@SuppressWarnings("1111") // 유효하지 않은 애너테이션은 무시된다. 
@TestInfo(testedBy="aaa", testTools = {"JUnit","JUnit5"},testDate = @DateTime(yymmdd="160101",hhmmss="235959"))
//@TestInfo에서 default값은 적지 않아도 됨
class Ex12_8 {
	public static void main(String args[]) {
		Class<Ex12_8> cls = Ex12_8.class;
		
		TestInfo anno = cls.getAnnotation(TestInfo.class);
		System.out.println("anno.testedBy() = "+anno.testedBy());
		System.out.println("anno.testDate().yymmdd()=" + anno.testDate().yymmdd());
		System.out.println("anno.testDate().hhmmss()=" + anno.testDate().hhmmss());
		
		for(String str:anno.testTools()) 
			System.out.println("testTools="+str);
		
		System.out.println();
		
		// Ex12_8에 적용된 모든 애너테이션을 가져온다. 
		Annotation[] annoArr = cls.getAnnotations();
		
		for(Annotation a : annoArr)
			System.out.println(a);
	}// main의 끝 
}