애노테이션 선언과 적용#
애노테이션 적용#
- 코틀린에서
@Deprecated
애노테이션은 대신할 수 있는 패턴을 제시할 수 있다.
@Deprecated("Use new(index) instead.", ReplaceWith("new(index)"))
fun deprecated(index: Int) {
println("deprecated: index")
}
fun new(index: Int) {
println("new: $index")
}

- 애노테이션의 인자로는 원시 타입의 값, 문자열, enum, 클래스 참조, 다른 애노테이션 클래스, 그리고 지금까지 말한 요소들로 이뤄진 배열이 들어갈 수 있다.
- 클래스 애노테이션 인자로 지정할 때는
@MyAnnotation(MyClass::class)
처럼 ::class
를 클래스 이름 뒤에 넣어야 한다. - 다른 애노테이션을 인자로 지정할 때는 인자로 들어가는 애노테이션의 이름 앞에
@
를 넣지 말아야된다. 앞에서 본 예제의 ReplaceWith
는 애노테이션이다. - 배열을 인자로 사용하는 경우 자동으로 가변 길이 인자로 변환된다.
- 애노테이션 인자를 컴파일 시점에 알 수 있어야 한다.
- 프로퍼티를 애노테이션 인자로 사용하려면 그 앞에
const
변경자를 붙여야한다.- 컴파일러는
const
가 붙은 프로퍼티를 컴파일 시점 상수로 취급한다. const
가 붙은 프로퍼티는 파일의 맨위나 object
안에 선언해야 하며, 원시 타입이나 String
으로 초기화해야만 한다.
애노테이션 대상#
- 사용 지점 대상 선언으로 애노테이션을 붙일 요소를 정할 수 있다.

- 사용 지점 대상을 지정할 때 지원하는 대상 목록은 다음과 같다.
property
: 프로퍼티 전체. 자바에서 선언된 애노테이션은 이 사용 지점 대상을 사용할 수 없다.field
: 프로퍼티에 의해 생성되는 (뒷받침하는) 필드get
: 프로퍼티 게터set
: 프로퍼티 세터receiver
: 확장 함수느 프로퍼티의 수신 객체 파라미터param
: 생성자 파라미터setparam
: 세터 파라미터delegate
: 위임 프로퍼티의 위임 인스턴스를 담아둔 필드file
: 파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스
file
대상을 사용하는 애노테이션은 package
선언 앞에서 파일의 최상위 수준에서만 사용할 수 있다.- 파일에 있는 최상위 선언을 담는 클래스의 이름을 바꿔주는
@file:JvmName("StringFunctions")
- 애노테이션 인자로 임의의 식을 허용한다.

- 자바 API를 애노테이션으로 제어하기
@JvmName
: 코틀린 선언이 만들어내는 자바 필드나 메소드 이름을 변경한다.@JvmStatic
: 메소드, 객체 선언, 동반 객체를 자바 정적 메소드로 노출한다.@JvmOverloads
: 디폴트 파라미터 값이 있는 함수에 대해 컴파일러가 자동으로 오버로딩한 함수를 생성해준다.@JvmField
: 프로퍼티에 사용하면 게터나 세터가 없는 public 자바 필드로 프로퍼티를 노출시킨다.
애노테이션 선언#
- 자바와 코틀린 애노테이션에서
value
는 특별하게 취급한다.- 애노테이션을 적용할 때
value
를 제외한 모든 애트리뷰트에는 이름을 명시해야 한다.
annotation class JsonExclude(val name: String)
메타애노테이션: 애노테이션을 처리하는 방법 제어#
- 메타애노테이션: 애노테이션 클래스에 적용할 수 있는 애노테이션
- 대표적은 메타애노테이션
@Target
- 애노테이션을 적용할 수 있는 요소의 유형을 지정한다.
PROPERTY
, METHOD
, ANNOTATION_CLASS
, FILED
등
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude(val name: String)
@Retention
애노테이션- 기본적으로 애노테이션은 .class 파일에는 저장되지만 런타임에는 사용할 수 없게 된다.
@Retention
은 정의 중인 애노테이션 클래스를 소스 수준에서만 유지할지, .class 파일에 저장할지, 실행 시점에 리플렉션을 사용해 접근할 수 있게 하지 지정하는 메타애노테이션이다.- 코틀린에서는 기본적으로 애노테이션의
@Retention
을 RUNTIME
으로 지정한다.
애노테이션 파라미터로 클래스 사용#
- 클래스 참조를 파라미터로 하는 애노테이션 클래스를 선언할 수 있다.
애노테이션 파라미터로 제네릭 클래스 받기#
- 제네릭 클래스를 파라미터로 하는 애노테이션 클래스를 선언할 수 있다.

리플렉션: 실행 시점에 코틀린 객체 내부 관찰#
- 리플렉션: 실행 시점에 객체의 프로퍼티와 메소드에 접근할 수 있게 해주는 방법
- 코틀린에서 리플렉션을 사용하려면 두 가지 서로 다른 리플렉션 API를 다뤄야 한다.
- 자바가
java.lang.reflection
패키지를 통해 제공하는 표준 리플렉션. - 크틸린이
kotlin.reflection
패키지를 통해 제공하는 코틀린 리플렉션. 현재 코틀린 리플렉션 API는 자바 리플렉션 APi를 완전히 대체할 수 있는 복잡한 기능을 제공하지는 않는다.
코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty#
KClass
를 사용하면 클래스 안에 있는 모든 선언을 열거하고 각 선언에 접근하거나 클래스의 상위 클래스를 얻는 등의 작업이 가능하다.MyCalss::class
라는 식을 쓰면 KClass
의 인스턴스를 얻을 수 있다.- 실행 시점에 객체의 클래스를 얻으려면 객체의
javaClass
프로퍼티를 사용해 객체의 자바 클래스를 얻어야 한다.

- 클래스의 모든 멤버 목록이
KCallable
인스턴스의 컬렉션이다. KCallable
은 함수와 프로퍼티를 아우르는 공통 상위 인터페이스다.call()
을 사용하면 함수나 프로퍼티의 게터를 호출할 수 있다.call()
메소드를 호출할 때 넘긴 인자와 원래 함수에 정의된 파라미터 개수가 맞아 떨어지지않으면 IllegalArgumentException
이 발생한다.

- 함수를 호출하기 위해 더 구체적인 메소드를 사용할 수도 있다.
KFunctionN
인터페이스는 컴파일러가 생성한 합성 타입이다. 따라서, kotlin.reflect
패키지에서 이런 타입의 정의를 찾을 수는 없다.call()
대신 invoke()
메소드를 호출할 떄는 인자 개수나 타입이 맞아 떨어지지 않으면 컴파일이 안된다. 따라서 KFunction
의 인자 타입과 반환 타입을 모두 다 안다면 invoke
메소드를 호출하는 게 낫다.
KProperty
의 call
메소드를 호출하면 프로퍼티의 게터를 호출한다.- 프로퍼티 인터페이스는 프로퍼티 값을 얻는 더 좋은 방법으로
get
메소드를 제공한다. - 최상위 프로퍼티는
KProperty0
인터페이스의 인스턴스로 표현되며 KProperty0
안에 인자가 없는 get
메소드가 있다. - 멤버 프로퍼티는
Kproperty1
인스턴스로 표현된다. 그 안에는 인자가 1개인 get
메소드가 들어있다. - 멤버 프로퍼티는 어떤 객체에 속해 있는 프로퍼티이므로 멤버 프로퍼티의 값을 가져오려면 객체 인스턴스를 넘겨야 한다.
- 최상위 수준이나 클래스 안에 정의된 프로퍼티만 리플렉션으로 접근할 수 있고 함수의 로컬 변수에는 접근할 수 없다.

애노테이션을 활용한 직렬화 제어#
KAnnotatedElement
인터페이스에는 annotations
프로퍼티가 있다.KProperty
는 KAnnotatedElement
를 확장하므로 프로퍼티의 모든 애노테이션을 얻을 수 있다.findAnnotation
함수는 인자로 전달받은 타입에 해당하는 애노테이션이 잇으면 그 애노테이션을 반환한다.- 클래스와 객체는 모두
KClass
클래스로 표현된다.- 객체에는
object
선언에 의해 생성된 싱글턴을 가리키는 objectInstance
라는 프로퍼티가 있다. KClass
가 일반 클래스를 표현한다면 createInstance
를 호출해서 새 인스턴스를 만들어야 한다.
JSON 파싱과 객체의 역직렬화#
KCallable.callBy()
각 파라미터에대한 인자의 매핑 정보를 함께 담아 호출한다.