Skip to main content

10. 애노테이션과 리플렉션

  • 애노테이션(annotation)리플렉션(reflection) 을 사용하면 제약을 벗어나서 미리 알지 못하는 임의 클래스를 다룰 수 있습니다.

10.1 애노테이션 선언과 적용#

  • 자바 프레임워크도 애노테션을 많이 사용하면, 코틀린 애노테이션도 개념은 마찬가지입니다.
  • 메타데이터를 선언에 추가하면 애노테이션을 처리하는 도구가 컴파일 시점이나 실행 시점에 적절한 처리를 해줍니다.

10.1.1 애노테이션 적용#

  • 코틀린은 자바와 같은 방법으로 애노테이션을 사용할 수 있습니다.
import org.junit.*
class MyTest {
@Test fun testTrue() { Assert.assertTrue(true) }
}
  • 애노테이션의 인자로는 원시 타입의 값, 문자열, enum, 클래스 참조, 다른 애노테이션 클래스, 그리고 지금까지 말한 요소들로 이뤄진 배열이 들어갈 수 있습니다.
  • 애노테이션 인자를 지정하는 문법은 자바와 약간 다릅니다.
    • 클래스를 이노테이션 인자로 지정할 때는 @MyAnnotation(MyClass::class)처럼 ::class를 클래스 이름 뒤에 넣어야 합니다.
    • 다른 애노테이션을 인자로 지정할 때는 인자로 들어가는 애노테이션의 이름 앞에 @를 넣지 않아야합니다.
    • 배열을 인자로 지정하려면 @RequestMapping(path = arrayOf("/foo", "/bar")) 처럼 arrayOf 함수를 사용합니다. @JavaAnnotationWithArrayValue("abc", "foo", "bar") 처럼 arrayOf 함수를 쓰지 않아도 됩니다.
  • 애노테이션 인자를 컴파일 시점에 알 수 있어야 하므로, 임의의 프로퍼티를 인자로 지정할 수는 없습니다.

10.1.2 애노테이션 대상#

  • 애노테이션을 붙일 때 이런 요소 중 어떤 요소에 애노테이션을 붙일지 표시할 필요가 있습니다.
  • 사용 지점 대상(use-site target) 선언으로 애노테이션을 붙일 요소를 정할 수 있습니다.
@get:Rule
// @get : 사용 지점 대상
// Rule : 애노테이션 이름
  • 사용 지점 대상을 지정할 때 지원하는 대상 목록은 다음과 같습니다.
이름설명
property프로퍼티 전체
field프로퍼티에 의해 생성되는 필드
get프로퍼티 게터
set프로퍼티 세터
receiver확장 함수나 프로퍼티의 수신 객체 파라미터
param생성자 파라미터
setparam세터 파라미터
delegate위임 프로퍼티의 위임 인스턴스를 담아둔 필드
file파일 안에 선언된 최상위 함수와 프로퍼티를 담아둔 클래스

10.1.3 애노테이션을 활용한 JSON 직렬화 제어#

  • 직렬화(serialization) : 객체를 저장장치에 저장하거나 네트워크를 통해 전송하기 위해 텍스트나 이진 변환하는 것
  • 역직렬화(deserialization) : 텍스트나 이진 형식으로 저장된 데이터로부터 원래의 객체를 만들어냅니다.
  • 대표적인 예시로 사용하는 JacksonGSON 이 있습니다.
data class Person(val name: String, val age: Int)
val person = Person("Alice", 29)
println(serialize(person)) // {"age": 29, "name": "Alice"}
val json = """{"name": "Alice", "age": 29}"""
println(deserialization<Person>(json)) // Person(name=Alice, age=29)
  • @JsonExclude 애노테이션을 사용하면 직렬화나 역직렬화 시 그 프로퍼티를 무시할 수 있습니다.
  • @JsonName 애노테이션을 사용하면 프로퍼티를 표현하는 키/값 쌍의 키로 프로퍼티 이름 대신 애노테이션이 지정한 이름을 쓰게 할 수 있습니다.
data class Person(
@JsonName("alias") val firstName: String,
@JsonExclude val age: Int? = null
)

10.1.4 애노테이션 선언#

  • 가장 간단한 애토네이션은 파라미터도 없는 경우입니다.
    • annotation class JsonExclude
  • 파라미터가 있는 애노테이션을 정의하려면 애노테이션 클래스의 주 생성자에 파라미터를 선언해야 함
    • annotation class JsonName(val name: Stirng)
  • 자바의 애노테이션은 다음과 같습니다.
public @interface JsonName {
String value();
}

10.1.5 매타애노테이션: 애노테이션을 처리하는 방법 제어#

  • 애노테이션 클래스에 적용할 수 있는 애노테이션을 메타애노테이션(meta-annotation) 이라고 부릅니다.
  • 표준 라이브러리에 있는 메타애노테이션 중 가장 흔히 사용하는 메타애노테이션은 @Target 입니다.
@Target(AnnoationTarget.PROPERTY)
annotation class JsonExclude
  • 메타애노테이션을 직접 만들어야한다면 ANNOTATION_CLASS를 대상으로 지정합니다.
@Target(AnnoationTarget.ANNOTATION_CLASS)
annotation class BindingAnnotation
@BindingAnnotation
annotation class MyBinding

@Retention 애노테이션

  • 정의 중인 애노테이션을 클래스를 소스 수준에서만 유지할지, .class 파일에 저장할지, 실행 시점에 리플렉션을 사용해 접근할 수 있게 할지를 지정하는 메타애노테이션

10.1.6 애노테이션 파라미터로 클래스 사용#

  • @DeserializeInterface는 인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 쓰는 애노테이션입니다.
interface Company { val name: String }
data class CompanyImpl(override val name: String) : Company
data class Person(
val name: String,
@DeserializeInterface(CompanyImpl::class) val company: Company
)
  • KClass는 자바 java.lang.Class 타입과 같은 역할을 하는 코틀린 타입입니다.
  • KClass의 타입 파라미터는 이 KClass의 인스턴스가 가리키는 코틀린 타입을 지정합니다.

10.1.7 애노테이션 파라미터로 제네릭 클래스 받기#

  • CustomSerializer 애노테이션은 커스텀 직렬화 클래스에 대한 참조를 인자로 받습니다.
  • 이 직렬화 클래스는 ValueSerializer 인터페이스를 구현해야만 합니다.
interface ValueSerializer<T> {
fun toJsonValue(value: T): Any?
fun fromJsonValue(jsonValue: Any?): T
}
  • serializationClass 파라미터의 타입에 대한 설명은 다음과 같습니다.
KClass<out ValueSerializer<*>>
// <out ValueSerializer<*>> : DateSerializer::class는 올바른 인자로 받아들이지만 Date::class는 거부합니다.
// out : ValueSerializer::class뿐 아니라 ValueSerializer를 구현하는 모든 클래스를 받아들입니다.
// <*> : ValueSerializer를 사용해 어떤 타입의 값이든 직렬화할 수 있게 허용합니다.
  • 리플렉션을 사용해 애노테이션에 저장된 데이터에 접근할 수 있습니다.

10.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰#

  • 리플렉션은 실행 시점에 객체의 프로퍼티와 메서드에 접근할 수 있게 해주는 방법
  • 코틀린에서 리플렉션을 사용하려면 두 가지 서로 다른 리플렉션 API를 다뤄야 합니다.
    • 첫번째는 자바가 java.lang.reflect 패키지를 통해 제공하는 표준 리플렉션
    • 두번째는 코틀린이 kotlin.reflect 패키지를 통해 제공하는 코틀린 리플렉션 API

10.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty#

10.2.2 리플렉션을 사용한 객체 직렬화 구현#

10.2.3 애노테이션을 활용한 직렬화 제어#

10.2.4 JSON 파싱과 객체 역직렬화#

10.2.5 최종 역직렬화 단계: callBy(), 리플렉션을 사용해 객체 만들기#


10.3 요약#

Last updated on