리플렉션(Reflection)은 사전적 의미로 투영, 반사라는 뜻이다. 프로그램에서 리플렉션은 런타임 때 프로그램의 구조(객체, 함수, 프로퍼티)를 분석해 내는 기법을 이야기한다. 예를 들어 어떤 함수를 정의하는데 함수의 매개변수로 클래스 타입을 선언하고 런타임 때 매개변수로 전달된 클래스의 이름, 클래스의 함수나 프로퍼티를 동적으로 팓단하는 작업을 리플렉션이라고 한다.
코틀린에서 리플렉션을 위해서는 라이브러리가 필요하다. kotlin-reflect.jar라는 라이브러리를 의존성 설정을 통해 준비해야 한다.
클래스 타입과 레퍼런스
런타임 때 동적으로 클래스를 분석하려면 클래스에 대한 정보가 필요한데 이 클래스에 대한 정보를 클래스 레퍼런스라고 표현하며, 클래스 레퍼런스를 대입받는 곳은 클래스 타입으로 선언해야 한다.
클래스 타입은 KClass<*>로 표현하며 이곳에 대입하는 클래스 레퍼런스는 “클래스명::class”로 표현한다.
리플렉션은 클래스, 함수, 프로퍼티의 레퍼런스를 런타임 때 동적으로 분석하는 목적이다. 따라서 레퍼런스를 분석하기 위한 다양한 방법을 제공한다.
클래스 정보 분석
클래스 기본 정보 분석
val isAbstract: Boolean
val isCompanion: Boolean
val isData: Boolean
val isFinal: Boolean
val isInner: Boolean
val isOpen: Boolean
val isSealed: Boolean
생성자 분석
클래스 분석 중 클래스가 생성자를 포함하고 있는지, 생성자의 매개변수는 어떻게 선언되어 있는지 분석.
val constructors: Collection<KFunction
val <T: Any> KClass
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
open class MyClass(no: Int) {
constructor(no: Int, name: String): this(10){}
constructor(no: Int, name: String, email: String): this(10){}
}
fun someFun(arg: KClass<*>){
val constructors = arg.constructors
for(constructor in constructors){
print("constructor.....")
val parameters = constructor.parameters
for(parameter in parameters){
print("${parameter.name}: ${parameter.type} ..")
}
println()
}
println("primary contructor.....")
val primaryConstructor = arg.primaryConstructor
if(primaryConstructor != null){
val parameters = primaryConstructor.parameters
for(parameter in parameters){
print("${parameter.name}: ${parameter.type} ..")
}
}
}
fun main(args: Array<String>){
someFun(MyClass::Class)
}
클래스 프로퍼티 분석
val <T: Any> KClass
val <T: Any> KClass
val <T: Any> KClass
val
클래스 함수 분석
val KClass<>.declaredMemberFunctions: Collection<KFunction<» 확장된 함수를 제외한 클래스에 선언된 모든 함수 반환
val KClass<>.memberFunctions: Collection<KFunction<» 확장 함수를 제외한 클래스와 상위 클래스에 선언된 모든 함수 반환
val KClass<>.declaredMemberExtensionFunctions: Collection<KFunction<» 클래스에 선언된 확장 함수 만을 반환
val KClass<>.memberExtensionFunctions: Collection<KFunction<» 상위 클래스 및 현 클래스의 확장 함수 만을 반환
함수 레퍼런스와 프로퍼티 레퍼런스
함수 레퍼런스
함수 레퍼런스는 “::함수명”을 이용한다. 예를 들어 myFun()이라는 함수의 레퍼런스를 명시하려면 ::myFun으로 지정한다. 이런 함수 레퍼런스는 KFunction<*> 타입으로 표현한다. 클래스에 선언한 멤버 함수의 레퍼런스는 앞에 클래스명을 추가하여 MyClass::myFun2로 표현한다.
val name: String 함수 이름
val parameters: List
val returnType: KType 함수의 반환 타입
KFunction 타입을 이용해 함수 레퍼런스를 이용하는 것은 함수의 기본 정보를 추출하는 것 이외에 고차 함수 이용에도 유용하다.
fun isOdd(x: Int): Boolean = x % 2 != 0
fun reflectionFun(argFun: (Int) -> Boolean){
println("result: ${argFun(3)}")
}
fun main(args: Array<String>){
reflectionFun { n -> isOdd(n) }
reflectionFun(::isOdd)
}
프로퍼티 레퍼런스
프로퍼티 레퍼런스는 “::프로퍼티명”으로 이용한다. 이런 프로퍼티 레퍼런스는 두 가지 타입으로 표현하는데 KProperty<>와 KMutableProperty<>이다. val로 선언한 프로퍼티는 KProperty, var로 선언한 프로퍼티는 KMutableProperty로 표현한다.
var로 선언한 프로퍼티의 레퍼런스는 KProperty와 KMutableProperty 타입 모두에 대입할 수 있지만, val로 선언한 프로퍼티의 레퍼런스는 KProperty에만 대입할 수 있다. 프로퍼티는 setter/getter를 포함하는 변수이므로 프로퍼티의 레퍼런스 타입을 이용하여 get(), set() 함수를 호출할 수 있다. 그런데 val로 선언한 프로퍼티의 레퍼런스로는 get() 함수만 호출할 수 있으며, var로 선언한 프로퍼티의 레퍼런스로는 get()과 set() 함수 모두 호출할 수 있다.
val getter: Getter
val isConst: Boolean
val isLateinit: Boolean
val isAbstract: Boolean
val isFinal: Boolean
val isOpen: Boolean
val name: String 프로퍼티 이름 추출
val returnType: KType 프로퍼티 타입 추출
fun call(vararg args: Any?): R 프로퍼티 get() 호출
val setter: Setter
val myVal: Int = 3
var myVar: Int = 5
class MyClass {
val objVal: Int = 10
var objVar: Int = 20
}
fun reflectionProperty(obj: Any?, arg: KProperty<*>){
println("property name: ${arg.name}, property type: ${arg.returnType}")
if(obj != null){
println(arg.getter.call(obj))
} else {
println(arg.getter.call())
}
}
fun reflectionMutableProperty(obj: Any?, arg: KMutableProperty<*>){
println("property name: ${arg.name}, property type: ${arg.returnType}")
if(obj != null){
arg.setter.call(obj, 40)
println(arg.getter.call(obj))
} else {
arg.setter.call(40)
println(arg.getter.call())
}
}
fun main(args: Array<String>) {
reflectionProperty(null, ::myVal)
reflectionMutableProperty(null, ::myvar)
val obj: MyClass = MyClass()
reflectionProperty(obj, MyClass::objVal)
reflectionMutableProperty(obj, Myclass::objVar)
}
reflectionProperty() 함수는 val로 선언한 프로퍼티의 정보를 분석하기 위한 함수로 KProperty의 name과 returnType을 이용하여 전달받은 프로퍼티의 이름과 타입을 얻는다. 그리고 프로퍼티의 get()함수를 호출하여 프로퍼티 값을 얻는데, 이 때 이용되는 것이 getter.call() 함수이다. 클래스 멤버의 경우 객체 정보도 함께 전달해야 하므로 getter.call(obj)로 작성한다. setter도 이와 비슷하다.