[Kotlin] Reflection에 관하여 & Property에 관하여
Java에서 Class class를 통하여 Reflection을 수행했었다.
Java와 완벽 호환되는 언어인 Kotlin은 이에 맞게 KClass를 지원한다.
예시로 KCallable과 KFunction을 살펴보도록 하자.
/**
* @param R return type of the callable.
*/
public actual interface KCallable<out R> : KAnnotatedElement {
public val parameters: List<KParameter>
public val returnType: KType
@SinceKotlin("1.1")
public val typeParameters: List<KTypeParameter>
public fun call(vararg args: Any?): R
public fun callBy(args: Map<KParameter, Any?>): R
@SinceKotlin("1.1")
public val visibility: KVisibility?
@SinceKotlin("1.1")
public val isFinal: Boolean
@SinceKotlin("1.1")
public val isOpen: Boolean
@SinceKotlin("1.1")
public val isAbstract: Boolean
@SinceKotlin("1.3")
public val isSuspend: Boolean
}
public actual interface KFunction<out R> : KCallable<R>, Function<R> {
@SinceKotlin("1.1")
public val isInline: Boolean
@SinceKotlin("1.1")
public val isExternal: Boolean
@SinceKotlin("1.1")
public val isOperator: Boolean
@SinceKotlin("1.1")
public val isInfix: Boolean
@SinceKotlin("1.1")
public override val isSuspend: Boolean
}
Callable은 call, callBy 함수 및 parameter 리스트와 parameter type 리스트,
리턴 타입과 여러 modifier에 대한 정보를 가지고 있다.
사용법은 다음과 같다.
"::"를 함수 명 앞에 붙이는 것으로 kFunction instance를 사용하고 있는 모습을 볼 수 있다.
kFunction의 타입이 kFunction0<Unit>으로 되어 있다.
함수의 파라미터를 2개로 늘려주자 KFunction2[23부터는 KFunctionN]가 된 것을 확인할 수 있다.
그리고 <Int, Int, Unit> 을 확인할 수 있다.
마지막 타입은 함수의 리턴타입이고 나머지는 순서대로 파라미터의 타입이다.
이렇듯 KFunction# 타입이면 call 대신에 invoke를 사용할 수 있다.
함수 파라미터의 의도와 다르게 입력하였을때 invoke에서 에러를 발생시키는 것을 확인 할 수 있다.
invoke는 call과 다르게 함수의 파라미터와 파라미터 타입, 리턴타입에 대한 안전성을 보장해준다.
invoke를 사용할 수 있는 상황이면 call 대신 invoke를 선택해 주는 것이 좋겠다.
이렇듯 Class 또한 reflection을 통해 런타임에 클래스 정보를 사용할 수 있다.
프로퍼티, 함수, 부모 클래스 등등의 클래스와 관련 정보 모두 접근 가능하니 해보면 된다.
이 중에 companion object에 대해서 조그만 살펴보려 한다.
그 동안 코틀린의 프로퍼티란 backing field와 accessor의 조합이라고 알고 있었다.
companion object는 자바 코드로 바꾸어 보면
TestClass 내부에 정의된 companion class에서 TestClass에 static으로 정의된 필드의
accessor가 구현되어 있는 것을 확인 할 수 있다.
이 말은 즉, companion class에는 필드가 존재하지 않는 것이므로 프로퍼티가 존재하지 않고
그 결과로 위 코드에서 println의 결과가 아무것도 나오지 않아야 한다.
하지만 위 메인을 실행시켜보면 s1, s2를 확인 할 수 있다.
스택오버플로우에 질문을 올렸더니 당연한 소리 한다는 답변과 싫어요를 받았다.
accessor와 property와 같은 것이라는 답변이었다.
https://kotlinlang.org/docs/properties.html#backing-fields
Properties | Kotlin
kotlinlang.org
In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier [ 위 사이트 글 중... ]
위 코틀린 공식 페이지에서 자료를 읽고 더 정확한 정보를 알기 전 까지는
accessor와 메모리 저장이 필요하여 field를 생성해야 할때 생성하는 코드까지가 프로퍼티의 범위로 하기로 했다.
이 글을 정리하면서
Reflection에서 사용한 interface들은 모두 KAnotatedElement를 포함하고 있다.
KAnotatedElement는 annotation 리스트를 가지도록 하는 녀석이다.
다음 글에서는 이 annotation 사용법에 대해서 알아보도록 하자.