Java, Kotlin Annotation에 관하여
in Programming Language on Kotlin
이 글을 쓰게된 발단
나는 최근 Kotlin In Action
이라는 책으로 공부하는 스터디를 운영하고 있다.
현재 10장을 진행하고 있고, 10장의 내용은 Annoation과 Reflection이다.
해당 주제에서 앞부분에 Annotation에 관한 내용이 나오는데, 이번 주 발표자이신 스터디원분께서 한 가지 질문을 하셨다.
질문한 내용의 발단은 책 441페이지에 있는 아래의 문구 때문인데 같이 한번 봐보도록 하자.
자바에서 value 메소드는 특별하다.
어떤 애노테이션을 적용할 때 value를 제외한 모든 애트리뷰트에는 이름을 명시해야한다.
질문을 하시면서 예제를 하나 주셨는데 해당 예제의 출처는 아래의 블로그에 있다.
나는 지금부터 이 질문에 대한 답을 하면서, Java의 Annotation과 Kotlin의 Annotation에 차이에 대해서도 공유하고자 한다.
자바 Annotation에서 value 메소드가 Special 하다는 말의 의미?
백문이 불여일타!
직접 애노테이션을 만들어서 정말로 value에는 이름을 생략할 수 있는지를 알아보도록 하자.
아래와 같은 애노테이션을 만들었다.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GreetingAnnotation {
String value();
String greeting() default "안녕하세요.";
}
이 애노테이션은 value
와 greeting
두 개의 애트리뷰트를 갖는다.
이 애노테이션을 사용한 클래스는 아래와 같다.
public class Application {
@GreetingAnnotation(value = "해리",greeting = "하이요")
public void sayHello() {
System.out.println("Hello");
}
}
value에는 “해리”를, greeting에는 “하이요”를 애노테이션 애트리뷰트에 값으로 준 것을 확인할 수 있다.
자 그러면 value를 제외한 모든 애트리뷰트에는 이름을 명시해야한다고 했으니, value라는 이름을 없애보자.
value라는 이름을 명시하지 않아도 될 것 같지만, 그렇지 않다.
그 이유는 자바에서 value 애트리뷰트의 이름을 생략할 수 있는 경우는 value 애트리뷰트 하나만
요소로 받을 수 있는 경우에 한정되기 때문이다.
그래서 다음과 같이 수정하고 실행해보자.
public class Application {
@GreetingAnnotation("해리")
public void sayHello() {
System.out.println("Hello");
}
}
public class Main {
public static void main(String[] args) {
Method[] methos = Application.class.getDeclaredMethods();
Application a = new Application();
for(Method method: methos){
if(method.isAnnotationPresent(GreetingAnnotation.class)){
GreetingAnnotation annotation = method.getAnnotation(GreetingAnnotation.class);
System.out.println(annotation.value());
System.out.println(annotation.greeting());
}
}
a.sayHello();
}
}
// output
해리
안녕하세요.
Hello
자바에서는 value의 이름이 생략가능한 경우는 하나의 애트리뷰트(value)만 설정하는 경우에 가능하다.
코틀린의 애노테이션 + 리플렉션
코틀린에서 애노테이션을 만드는 경우는 어떨까?
코틀린에서 위와 동일한 예제를 만들어서 실험해보자.
annotation class GreetingAnnotation (val value:String, val greeting:String="안녕하세요.")
애노테이션 선언은 class 앞에 annotation
을 붙여서 선언이 가능하며 이는 자바의 @interface
라는 다소 모호한 선언보다 조금 더 직관적이다.
애노테이션의 애트리뷰트들은 주생성자로 선언이 가능한데, 반드시 val
을 붙여야한다.
클래스는 다음과 같이 선언하였다.
class Application {
@GreetingAnnotation("해리","하이염")
fun sayHello() {
println("Hello")
}
}
위의 코드는 컴파일러 에러를 발생시키지 않는다.
자바와는 다르게 생성자를 기반으로 하기 때문에 애노테이션에 명시적으로 애트리뷰트의 이름을 명시하지않아도 사용할 수 있다는 점이 주목할만 하다.
이제 코틀린 리플렉션을 사용해서 해당 애노테이션의 값을 가져와보자.
fun main() {
val methods = Application::class.declaredFunctions
for(method in methods){
val annotation = method.findAnnotation<GreetingAnnotation>() ?: return
println(annotation.value)
println(annotation.greeting)
}
}
// output
해리
하이염
코틀린 리플렉션과 코틀린 애노테이션을 사용하여 값을 가져와보았다.
여담이지만 코드를 보면 자바에 비해 상당히 직관적이고 깔끔하다는 것을 알 수 있을 것이다.
지금까지 코틀린에서의 애노테이션 사용법을 간략하게 살펴보았다.
그런데 자세히 살펴보면, 코틀린 애노테이션은 value 뿐만 아니라 다른 애트리뷰트들의 이름을 명시하지 않아도 허용해주고 있다.
이는 코틀린의 애노테이션이 생성자 기반이기 때문인데, 그렇다면 처음 문제였던 value 이외의 애트리뷰트에는 이름을 명시해야한다는 경우는 어떤 경우를 말하는걸까?
그 답은 아래에 있다.
코틀린에서 자바 애노테이션을 사용하는 경우
Kotlin In Action 10장 441페이지에는 다음과 같은 내용이 나온다.
코틀린에서 자바 애노테이션을 사용할 경우 value를 자바와 동일하게 Special하게 취급해준다
이를 위해 코틀린 코드에서 자바 애노테이션을 만들고 사용해보았다.
GreetingAnnotation이 코틀린으로 만든 애노테이션이고, GreetingAnnotationJava는 자바로 만든 애노테이션이다.
value의 경우 애트리뷰트의 이름을 명시하지않아도 되지만, value 이외의 애트리뷰트들은 반드시 이름을 명시할 것을 컴파일러가 경고하고 있다.
그렇다. 이것이 책에서 말한 value 이외의 애트리뷰트에는 이름을 명시해야한다는 의미였던 것이다.
코틀린에서 자바 애노테이션을 사용할 경우 자바와는 달리, value 외의 다른 애트리뷰트를 사용하더라도 value는 생략해주도록 만들어졌다는 것을 알 수 있다.
이를 통해 코틀린 역시 자바와 마찬가지로 value를 Special하게 취급해준다는 사실을 알 수 있다.
결론
애노테이션에서 value 이외의 애트리뷰트에는 이름을 명시해야한다는 의미는 코틀린에서 자바 애노테이션을 사용할 때를 의미한다.
자바 애노테이션에서의 value의 이름을 생략할 수 있는 경우는 애노테이션에 전달하는 애트리뷰트가 value 하나인 경우에만 가능하다.
자바를 잘 알고 있다는 것을 전제로 책이 만들어졌다보니 이런 부분에서 오해할 수 있겠다는 생각을 하였다.
이번 계기로 필자 역시 코틀린 애노테이션과 리플렉션을 사용해볼 수 있어서 무척이나 유익하고 재밌는 시간이었다.
이 글을 읽는 여러분들에게도 즐거운 시간이었기를 바라면서 글을 마치도록 하겠다.
Reference
Kotlin In Action (드미트리 제메로프, 스베트라나 이사코바)
예제 : https://yookeun.github.io/java/2017/01/13/java-annotation/