카테고리 없음

[Kotlin] Reflection에 관하여 & Property에 관하여

Jun.LEE 2024. 4. 15. 21:56

Java에서 Class class를 통하여 Reflection을 수행했었다.

Java와 완벽 호환되는 언어인 Kotlin은 이에 맞게 KClass를 지원한다.

 

Kotlin in action, 268p, Figure 10.6

 

예시로 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 사용법에 대해서 알아보도록 하자.