관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch12 - 23 ~ 37 [애노테이션, 표준애노테이션, 메타애노테이션, 애노테이션 타입 정의하기, 애노테이션의 요소] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch12 - 23 ~ 37 [애노테이션, 표준애노테이션, 메타애노테이션, 애노테이션 타입 정의하기, 애노테이션의 요소]

소소한나구리 2023. 12. 15. 11:55

1) 애노테이션

  • 주석처럼 프로그래밍 언어에 영향을 미치지 않으며 유용한 정보를 제공함
  • 특정 프로그램에 정보 제공을 위한 것(설정 정보 등)
  • Annotation 은 인터페이스

Annotation.java 파일의 앞부분, /** ~ */ = Javadoc 주석(Javadoc이라는 프로그램을 위한 주석)

  • 애노테이션 사용 예시

JUnit(단위테스트 프레임워크)에 Test메서드임을 알림


 

2) 표준 애노테이션

  • Java에서 제공하는 애노테이션의 종류
  • 하얀색 - 표준애노테이션
  • 갈색 - 매타애노테이션 : 애노테이션을 만들 때 사용

(1) @Override

  • 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 함
  • 오버라이딩 할 때 메서드 이름을 잘못적는 실수를 하는 경우가 많은데 이를 방지할 수 있음
  • 오버라이딩 할 때는 메서드 선언부 앞에 @Override를 붙이는 것을 권장
class Parent {
	void parentMethod() {}
}
class Child extends Parent {
	void parentmethod() {}	// 오버라이딩 하려 했으나 실수로 이름을 잘못 적음
}

//Override 애노테이션 추가하여 오버라이딩이 제대로 되었는지 체크	 
class Child extends Parent {
	@Override
	void parentmethod() {}	// 에러 발생. 조상의 메서드의 이름을 잘못적음	
}

 

(2) @Deprecated

  • 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙임
  • @Deprecated의 사용 예시로 Date클래스의 getDate() 메서드가 있음
  • @Deprecated가 붙은 대상이 사용된 코드를 사용하면 IDE 상에서 해당 코드에 줄이 그어지고 deprecated가 적용된 메서드가 있으니 컴파일하면 다시 컴파일하라는 메세지가 나타남(터미널에서 강제 컴파일)
@Deprecated
public int getDate() {	// 사용을 권장하지 않음
	return normalize(). getDayOfMonth();
}
좌 / eclipse에서 Deprecated 사용, 우 / 터미널에서 컴파일 후 나타나는 메세지

(3) @FunctionalInterface

  • 함수형 인터페이스에 사용함
  • 함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이 있는데 이를 올바르게 작성했는지 체크함
@FunctionalInterface	// Override 애노테이션 처럼 함수형 인터페이스 작성시 작성을 권장
public interface Runnable {
	public abstract void run ();	// 추상메서드
}

@FunctionalInterface 	// 함수형 인터페이스인지 체크
interface Testable {	// 함수형 인터페이스는 추상메서드를 1개만 가질 수 있음 -> 에러발생
	void test();	// 추상메서드
	void check();	// 추상메서드
}

 

(4) @SuppressWarnings

  • 컴파일러의 경고메세지가 나타나지 않게 억제함
  • 괄호()안에 억제하고자 하는 경고의 종류를 문자열로 지정할 수 있고 둘 이상의 경고를 동시에 억제할 수 있음
@SuppressWarnings("unchecked")	// 제네릭스와 관련된 경고를 억제
// ArrayList = 제네릭클래스 ArrayList<T> 처럼 작성해야함
ArrayList list = new ArrayList();	// 제네릭 타입을 지정하지 않았음
list.add(obj);	// 여기서 경고가 발생함	

// 경고를 확인했다는 의미로 사용

// 둘이상의 경고를 동시에 억제
@SuppressWarnings({"deprecation", "unchecked", "varargs"})

// [deprecation] 경고 억제
class Ex12_7 {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		Child c = new Child();
		c.parentMethod();
	}
}

 

좌 / [deprecation] 경고  우/ 경고를 억제 후 컴파일 (경고가 없어짐)

3) 메타 애노테이션 

  • 애노테이션을 위한 애노테이션으로 애노테이션을 정의할 때 사용함
  • java.lang.annotation 패키지에 포함되어 있음

(1) @Target

  • 애노테이션을 정의할 수 있는 적용할 대상을 지정할 때 사용
  • ANNOTATION_TYPE : 애노테이션
  • CONSTRUCTOR : 생성자
  • FIELD : 필드(멤버변수, enum 상수)
  • LOCAL_VARIABLE : 지역 변수
  • METHOD : 메서드
  • PACKAGE : 패키지
  • PARAMETER : 매개변수
  • TYPE : 타입(클래스, 인터페이스, enum)
  • TYPE_PARAMETER : 타입 매개변수(JDK 1.8 이후)
  • TYPE_USE : 타입이 사용되는 모든 곳 (JDK 1.8 이후)
SuppressWarnings 애노테이션의 실제 소스

 

import static java.lang.annotation.ElementType.*;

@Target({FIELD, TYPE, TYPE_USE})	// 적용 대상을 지정
public @interface MyAnnotation { }	// MyAnnotation을 정의

@MyAnnotation	// 적용 대상이 TYPE인 경우
class MyClass {	// 클래스나 인터페이스

@MyAnnotation	// 적용 대상이 FIELD인 경우
int i;	// iv멤버변수

@MyAnnotation	// 적용 대상이 TYPE_USE인 경우
MyClass me;	// 참조변수

 

(2) @Retention

  • 애노테이션이 유지(Retention)되는 기간을 지정하는데 사용, 즉 애노테이션의 생명주기를 설정
  • 컴파일러에 의해 사용되는 애노테이션의 유지 정책은 SOURCE(소스파일에만 존재)
  • 실행시에 사용 가능한 애노테이션의 정책은 RUNTIME(class파일에 존재)
  • 기본정책은 CLASS(클래스 파일에 존재)

 

(3) @Documented, @Inherited

  • javadoc으로 작성한 문서에 포함시키려면 @Documented를 붙힘
  • 애노테이션을 자손 클래스에 상속하고자 할 때, @Inherited를 붙힘
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

@Inherited // @SuperAnno가 자손까지 영향 미치게
@interface SuperAnno {}

@SuperAnno
class Parent {}

class Child extends Parent {} // Child에 애너테이션 붙은 것으로 인식

 

(4) @Repeatable 

  • 반복해서 붙일 수 있는 애노테이션을 정의할 때 사용
  • @Repeatable인 @ToDo를 하나로 묶을 컨테이너 애노테이션도 정의해야 함 
@Repeatable(ToDos.class)	
@interface ToDo {	// ToDo애노테이션을 여러 번 반복해서 쓸 수 있게 함.
	String value();
}

// 애노테이션 반복 사용
@ToDo("delete test coded.")
@ToDo("override inherited methods")
class MyClass {
	...
}

// 컨테이너 애노테이션 정의
@interface ToDos {	// 여러개의 ToDo애노테이션을 담을 컨테이너 애노테이션
	ToDo[] value();	// ToDo애노테이션 배열타입의 요소를 선언. 이름이 반드시 Value이어야 함
}

4) 애노테이션 타입 정의하기 

  • 애노테이션을 직접 만들어서 사용할 수 있음
  • 애노테이션의 메서드는 추상메서드이므로 구현할 필요가 없으며 애노테이션을 적용할 때 각 추상메서드의 값을 지정하여 입력(입력하는 순서는 상관 없음)

(1) 애노테이션 정의 예시

// 애노테이션 정의방법
@interface 애노테이션이름 {
	타입요소이름();	// 애노테이션의 요소를 선언
	...
}

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

// 애노테이션 사용 -> 요소의 값들을 전부 적어야 함
@TestInfo(
	count = 3, testedBy = "kim",
	testTools = {"JUnit", "AutoTester"},   // 배열은 배열로 작성
	testType = TestType.FIRST,            // 열거형 지정
	testDate = @DateTime(yymmdd="160101", hhmmss="235959")	
)
public class NewClass { ... }

 

5)  애노테이션의 요소

  • 적용시 값을 지정하지 않을 때 기본으로 사용될 수 있는 기본값을 지정할 수 있음(null 제외)
  • 요소가 하나이고 이름이 value일 때는 요소의 이름을 생략할 수 있음
  • 요소의 타입이 배열인 경우, 괄호{ }를 사용해야하며 마찬가지로 기본값을 적용할 수 있음
// 기본값을 지정할 수 있음
@interface TestInfo {
	int count() default 1;	// 기본값을 1로 지정
}

@TestInfo	// 적용값을 생략했으므로 기본값이 적용 됨
public class NewClass {...}

---------------------------------

// 요소가 하나이고 이름이 value일 때는 요소의 이름을 생략할 수 있음
@interface TestInfo {
	String value();
}
@TestInfo("passed")	// @TestInfo(value = "passed")와 동일
public class NewClass {...}

---------------------------------

// 배열인 경우 괄호 {} 사용하여 요소의 값을 입력해야함
@interface Test{
	String[] testTools();
}

@Test(testTools={"JUnit", "AutoTester"})
@Test(testTools={"JUnit"})
@Test(testTools={})	// 값이 없을 때에도 괄호{}가 반드시 필요

 

6) 모든 애노테이션의 조상 - java.lang.annotation.Annotation

  • Annotation은 모든 애노테이션의 조상이지만 상속할 수 없음
  • Annotation은 인터페이스
  • 추상메서드들은 구현할 필요 없이 컴파일러가 자동으로 구현하기 때문에 사용가능함

 

(1) 마커 애노테이션 (Marker Annotation)

  • 요소가 하나도 정의되지 않은 애노테이션
  • 애노테이션이 붙어있는 것만으로도 충분한 정보를 제공함
좌 / 마커애노테이션. 우/ 마커애노테이션 사용

(2) 애노테이션 요소의 규칙

  • 애노테이션의 요소를 선언할 때는 규칙을 반드시 지켜야 함
  • 요소의 타입은 기본형, String, enum, 애노테이션, class만 허용됨
  • 괄호()안에 매개변수를 선언할 수 없음 (추상메서드안에 매개변수 작성 불가)
  • 예외를 선언할 수 없음
  • 요소를 타입 매개 변수로 정의할 수 없음 즉, 제네릭스사용이 불가능함
@interface AnnoTest {
	int id = 100;                       // 상수 OK(인터페이스 처럼 static final 생략)
	String majer(int i, int j);         // 에러 : 매개변수 불가능
	String minor() throws Exception;    // 에러 : 예외선언 불가능
	ArrayList<T> list();	            // 에러 : 타입 매개변수 정의 불가능
}

 

(3) 애노테이션 예제

import java.lang.annotation.*;

@Deprecated
@SuppressWarnings("1111") // "1111" 경고는 무시
@TestInfo(testedBy="aaa", testDate=@DateTime(yymmdd="160101",hhmmss="235959"))
class Ex12_8 {
	public static void main(String args[]) {
		// Ex12_8의 Class객체를 생성
		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의 끝
}

@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 }

 

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의