목표
자바의 애노테이션에 대해 학습하세요.
학습할 것 (필수)
- 애노테이션 정의하는 방법
- @retention
- @target
- @documented
- 애노테이션 프로세서
애노테이션 정의하는 방법
애노테이션이란?
애노테이션의 사전적 의미는 주석, 메모라는 뜻이다.
자바를 개발한 사람들은 소스코드에 대한 문서를 따로 만들기보다 소스코드를 하나의 파일로 관리하는 것이 낫다고 생각했다. 그래서 소스코드의 주석 /** ~ */에 소스코드에 대한 정보를 저장하고, 소스코드의 주석으로부터 HTML 문서를 생성해내는 프로그램(javadoc.exe)을 만들어서 사용했다.
애노테이션은 주석(comment)처럼 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있다는 장점이 있다.
애노테이션 만들기
애노테이션을 정의하는 방법은 아래와 같다. '@'기호를 붙이는 것을 제외하면 인터페이스를 정의하는 것과 동일하다.
public @interface annoName {
}
애노테이션에 값 주기
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface annoName {
String name();
int number(); default 100; /* 기본값을 줄 수도 있다.*/
}
@annoName( name = "hyang") 이렇게 넘기면된다.
표준 애노테이션
자바에서 기본적으로 제공하는 애노테이션들이다. 메타 애노테이션도 자바 표준 애노테이션이다.
@Override : 컴파일러에게 오버 라이딩하는 메서드라는 것을 알린다.
메소드 앞에만 붙일 수 있는 애노테이션으로, 조상 메서드를 오버 라이딩하는 것을 컴파일 시점에 체크한다.
오버 라이딩할 때 메서드 앞에 @Override를 붙이는 것은 필수는 아니지만 알아내기 어려운 실수를 미연에 방지해주므로
반드시 붙이도록 하자.
@Deprecated : 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다.
새로운 버전의 JDK가 소개될 때, 새로운 기능이 추가될 뿐만 아니라 기존의 부족했던 기능 등을 개선하기도 한다. 이 과정에서 기존의 기능을 대체할 것들이 추가되어도, 이미 여러 곳에서 사용되고 있을지 모르는 기존의 것들은 함부로 삭제할 수 없다. 그래서 생각해낸 방법이 더 이상 사용되지 않은 필드나 메서드에 @Deprecated를 붙이는 것이다.
@SuppressWarnings : 컴파일러의 특정 경고 메시지가 나타나지 않게 해 준다.
컴파일러가 보여주는 경고 메시지가 나타나지 않도록 억제해준다.
@SafeVarargs : 지네릭스 타입의 가변 인자에 사용한다.
메서드에 선언된 가변 인자의 타입이 non-reifiable타입일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 "unchecked"경고가 발생한다. 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 @SafeVarargs를 사용해야 한다.
@FunctionalInterface : 함수형 인터페이스라는 것을 알린다. (8주 차 스터디)
추상 메소드를 딱 한 가지만 가지고 있는 인터페이스를 의미한다. (추상 메소드가 아니라면 괜찮다.)
이러한 함수형 인터페이스를 SAM (Single Abstract Method) 인터페이스라고도 한다.
@FunctionalInterface 애노테이션을 붙이면 컴파일 시점에 checking 한다.
@FunctionalInterface
public interface RunSomething {
void doIt();
static void printName() {
System.out.println("hyangkeun");
}
default void printAge() {
System.out.println("28");
}
}
메타 애노테이션
@Native : natvie메서드에서 참조되는 상수 앞에 붙인다.
네이티브 메서드에 의해 참조되는 상수 필드에 붙이는 애노테이션이다.
네이티브 메서드는 JVM이 설치된 OS의 메서드를 말한다. 네이티브 메서드는 보통 C언어로 작성되어 있는데 자바에서는 메서드의 선언부만 정의하고 구현은 하지 않는다. 그래서 추상메서드처럼 선언부만 있고 몸통이 없다.
자바에서는 정의된 네이티브 메서드와 OS메서드를 연결해주는 작업이 추가로 필요하다 이 역할은 JNI(Java Native Interface)가 한다.
JNI 사용하기 참고하기 좋은 글
@Target : 애노테이션이 적용 가능한 대상을 지정하는 데 사용한다.
Target: 애노테이션이 어디에 사용할 수 있는가?
예시 : @Target({ElementType.TYPE, ElementType.FIELD}) => 타입과 필드에만 애노테이션을 붙일 수 있다.
애너테이션에 적용할 수 있는 대상이 여러 개의 값을 지정해야 할 경우 배열에서처럼 괄호{}를 사용해야 한다.
📌@Target으로 지어할 수 있는 애너테이션 적용대상 종류는 아래와 같다.
- ANNOTATION_TYPE : 애노테이션
- CONSTRUCTOR : 생성자
- FIELD : 필드 (멤버 변수, enum 상수)
- LOCAL_VARIABLE : 지역변수
- METHOD : 메서드
- PACKAGE : 패키지
- PARAMETER : 매개변수
- TYPE : 타입(클래스, 인터페이스, enum)
- TYPE_PARAMETER : 타입 매개변수 ( 타입 변수에만 사용할 수 있다.)
- TYPE_USE : 타입이 사용되는 모든 곳 ( 타입 변수를 포함해서 모든 타입 선언부에 사용할 수 있다.)
Chicken.java(중복 사용할 애노테이션)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Repeatable(ChickenContainer.class)
public @interface Chicken {
String value();
}
ChickenContainer.java (중복 애노테이션의 컨테이너 애노테이션)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface ChickenContainer {
Chicken[] value();
}
컨테이너 애노테이션으로 중복 애노테이션 참조하기
@Chicken("양념")
@Chicken("마늘간장")
public class App {
public static void main(String[] args) {
ChickenContainer chickenContainer = App.class.getAnnotation(ChickenContainer.class);
Arrays.stream(chickenContainer.value()).forEach(c -> {
System.out.println(c.value());
});
}
}
@Documented : 애노테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다.
애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다. 자바에서 제공하는 기본 애노테이션 중에
@Override와 @SuppressWarnings를 제외하고는 모두 이 메타 애노테이션이 붙어있다.
IntelliJ에서 javadoc 만드는 법
shift 두 번 누른다 - Generate JavaDoc선택
🎇미리 저장할 경로에 폴더를 하나 만든다.
Output Directory에서 미리 만들어둔 경로를 선택한다.
other command line arguments 부분에 한글 설정을 한다
-encoding UTF-8 -charset UTF-8 -docencoding UTF-8
생성 완료
@Inherited : 애노테이션이 자손 클래스에 상속되도록 한다.
애노테이션이 자손 클래스에 상속되도록 한다. '@Inherited'가 붙은 애노테이션은 조상 클래스에 붙으면, 자손 클래스도 이 애노테이션이 붙은 것과 같이 인식된다.
@Inherited
@interface SuperAnno {}
@SuperAnno
class Parent {}
class Child extends Parent {} // Child에 @SuperAnno가 붙은 것으로 인식한다.
@Retention : 애노테이션이 유지되는 범위를 지정하는 데 사용한다.
해당 애노테이션을 언제까지 유지할 것인가? 소스(SOURCE), 클래스(CLASS), 런타임(RUNTIME)으로 제한 가능
- SOURCE : 소스 파일에만 존재, 클래스 파일에는 존재하지 않음
- CLASS : 클래스 파일에 존재, 실행 시에 사용불가. 기본값
- RUNTIME : 클래스 파일에 존재, 실행시에 사용 가능
@Repeatable : 애노테이션을 반복해서 적용할 수 있게 한다.
보통은 하나의 대상에 한 종류의 애너테이션을 붙이게 된다
그러나 @Repeatable이 붙은 애너테이션은 여러 번 붙일 수 있다.
마커 애노테이션
애노테이션의 요소
애너테이션 내에 선언된 메서드를 애너테이션의 요소(element)라고 하며, 아래 선언된 TestInfo애노테이션은 다섯 개의 요소를 갖는다.
@interface TestInfo {
int count();
String testedBy();
String[] testTools();
TestType testType();
DateTime testDate();
}
어노테이션 요소의 규칙
1. 요소의 타입은 기본형, String, enum, 어노테이션, Class만 허용된다.
2. () 안에 매개변수는 선언할 수 없다.
3. 예외를 선언할 수는 없다.
4. 요소를 타입 매개변수로 정의할 수 없다.
마커 애노테이션은 값을 지정할 필요가 없는 경우, 애너테이션의 요소(element)를 하나도 정의하지 않을 수 있다.
@Override처럼 요소가 하나도 정의되지 않은 애노테이션을 마커 애노테이션이라고 한다.
마커 애노테이션의 예) 정의된 요소가 없다
reifiable vs non-reifiable
reifiable은 '리어화이어블'이라고 읽으며 컴파일 후에 타입 정보가 유지되면 reifiable타입이라고 한다.
제거되는 타입이면 non-reifiable타입이라고 한다.
자바 리플렉션
리플렉션의 사전적인 의미는 투영, 반사라는 의미를 가진다.
리플렉션이란 객체를 통해 클래스의 정보를 분석해 내는 프로그램 기법을 말한다.
- 모든 클래스를 로딩 한 다음 Class의 인스턴스가 생긴다. “타입.class”로 접근할 수 있다.
- Class<Book> bookClass = Book.class;
- 모든 인스턴스는 getClass() 메소드를 가지고 있다. “인스턴스.getClass()”로 접근할 수 있다.
- Class<? extends Book> aClass = book.getClass();
- 클래스를 문자열로 읽어오는 방법
- Class.forName(“FQCN”)
- Class.forName("me.whiteship.Book");
- Class.forName(“FQCN”)
Class를 통해 할 수 있는 것
- 필드 (목록) 가져오기 : getFields()는 public 한 것만 return 하고, getDeclaredField()는 다 가져온다.
- 메소드 (목록) 가져오기
- 상위 클래스 가져오기
- 인터페이스 (목록) 가져오기
- 애노테이션 가져오기
- 생성자 가져오기
애노테이션 프로세서
애노테이션 프로세서는 소스코드 레벨에서 소스코드에 붙어있는 애노테이션 정보를 읽어서 컴파일러가 컴파일을 하는 중에 새로운 소스코드를 생성하거나 기존의 코드를 바꾸는(권장하지 않음) 또는 클래스의 바이트 코드를 생성 또는 리소스 파일을 생성하는 기능이다.
애노테이션 프로세서 장점
- 런타임 비용이 제로
애노테이션 프로세서 단점
- 기존 클래스 코드를 변경할 때는 약간의 hack이 필요하다
Reference
자바의 정석
백기선: 더 자바 코드를 조작하는 다양한 방법
'Java' 카테고리의 다른 글
(스터디 할래) 15주차 과제: 람다식 (0) | 2021.03.05 |
---|---|
스터디 할래 14주차 과제: 제네릭(feedback, 피드백) (0) | 2021.02.28 |
스터디 할래 12주차 과제: 애노테이션(피드백, feedback) (0) | 2021.02.14 |
스터디할래 11주차 과제: Enum(feedback, 피드백) (0) | 2021.02.13 |
11주차 과제: Enum (0) | 2021.02.13 |