티스토리 뷰

리플렉션(Reflection)은 런타임 때 프로그램의 구조(객체, 함수, 프로퍼티)를 분석해 내는 기법을 이야기합니다. 코틀린에서 리플렉션을 위해서는 라이브러리가 필요합니다. kotlin-reflect.jar 라는 라이브러리를 의존성(dependency) 설정을 통해 준비해야 합니다

 

dependencies {

  compile "org.jetbrains.kotlin:kotlin-reflect"

}

 

1. 클래스 타입과 레퍼런스

 

런타임 때 동적으로 클래스를 분석하려면 클래스에 대한 정보가 필요한데 이 클래스에 대한 정보를 클래스 레퍼런스라고 표현하며, 클래스 레퍼런스를 대입하는 곳은 클래스 타입으로 선언해야 합니다. 클래스 타입은 KClass<*>로 표현하며 대입하는 클래스 레퍼런스는 "클래스명::class"로 표현합니다

 

// KClass<*> 타입

val myVal: KClass<*> = String::class

 

fun myFun(arg: KClass<*>) {

}

 

위 소스에서 클래스 타입을 KClass<*>로 작성했는데 이 타입에는 어떤 클래스 레퍼런스도 대입할 수 있습니다. 예를 들어, KClass<String>으로 명시하면 String 클래스의 레퍼런스만 등록할 수 있습니다. 또한, 클래스 레퍼런스는 코틀린 클래스와 자바 클래스가 다른데 코틀린 클래스는 "클래스명::class"로 표현하며 자바 클래스는 "클래스명::class.java"로 표현합니다

 

// 클래스 타입

val myVal: KClass<String> = String::class

val myVal2: KClass<String> = Double::class // error

val myVal3: Class<*> = String::class.java

 

위와 같이 자바 클래스를 받으려면 클래스 타입을 Class<*>로 선언해야 합니다

 

2. 클래스 정보 분석

 

// 클래스 정보 분석

open class MyClass

 

fun someFun(arg: KClass<*>) {

  println("class info")

  println("isAbstract : ${arg.isAbstract}") // 클래스가 abstract로 선언되었는지 판단

  println("isCompanion : ${arg.isCompanion}") // 클래스가 companion로 선언되었는지 판단

  println("isData : ${arg.isData}") // 클래스가 data로 선언되었는지 판단

  println("isFinal : ${arg.isFinal}") // 클래스가 final로 선언되었는지 판단

  println("isInner : ${arg.isInner}") // 클래스가 inner로 선언되었는지 판단

  println("isOpen : ${arg.isOpen}") // 클래스가 open으로 선언되었는지 판단

  println("isSealed : ${arg.isSealed}") // 클래스가 sealed로 선언되었는지 판단

}

 

someFun(MyClass::class)

 

// class info

// isAbstract : false

// isCompanion : false

// isData : false

// isFinal : false

// isInner : false

// isOpen : true

// isSealed : false

 

3. 생성자 분석

 

// 생성자 정보 분석

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()

  }

 

  print("primary constructor...")

  val primaryConstructor = arg.primaryConstructor // 주 생성자 정보 

  if (primaryConstructor != null) {

    val parameters = primaryConstructor.parameters

    for (parameter in parameters) {

      print("${parameter.name}: ${parameter.type} .. ");

    }

  }

}

 

someFun(MyClass::class)

 

// constructor...no: kotlin:Int .. name: kotlin:String ..

// constructor...no: kotlin:Int .. name: kotlin:String .. email: kotlin.String ..

// constructor...no: kotlin:Int .. 

// primary constructor...no: kotlin:Int ..

 

4. 클래스 프로퍼티 분석

 

open class SuperClass {

  val superVal: Int = 10

}

 

class MyClass(val no: Int): SuperClass() {

  val myVal: String = "hello"

  val String.someVal: String

    get() = "world"

}

 

fun someFun(arg: KClass<*>) {

  // 확장 프로퍼티를 제외한 클래스에 선언된 모든 프로퍼티 반환

  val properties = arg.declaredMemberProperties

  println("declaredMemberProperties")

  for (property in properties) {

    println("${property.name}: ${property.returnType} .. ")

  }

 

  // 확장 프로퍼티를 제외한 클래스와 상위 클래스에 선언된 모든 프로퍼티 반환

  val properties2 = arg.memberProperties

  println("memberProperties")

  for (property in properties2) {

    println("${property.name}: ${property.returnType} .. ")

  }

 

  // 클래스에 선언된 확장 프로퍼티 반환

  val properties3 = arg.declaredMemberExtensionProperties

  println("declaredMemberExtensionProperties")

  for (property in properties3) {

    println("${property.name}: ${property.returnType} .. ")

  }

}

 

someFun(MyClass::class)

 

// declaredMemberProperties

// myVal: kotlin.String ..

// no: kotlin.Int ..

// memberProperties

// myVal: kotlin.String ..

// no: kotlin.Int ..

// superVal: kotlin.Int ..

// declaredMemberExtensionProperties

// someVal: kotlin.String ..

 

5. 클래스 함수 분석

 

// 함수 정보 분석

open class SuperClass {

  fun superFun() {}

}

 

class MyClass: SuperClass() {

  fun myFun() {}

  fun String.someFun() {}

}

 

fun someFun(arg: KClass<*>) {

  // 확장 함수를 제외한 클래스에 선언된 모든 함수 반환

  val functions = arg.declaredMemberFunctions

  println("declaredFunctions")

  for (function in functions) {

    println("${function.name}: ${function.returnType} .. ")

  }

 

  // 확장 함수를 제외한 클래스와 상위 클래스에 선언된 모든 함수 반환

  val functions2 = arg.memberFunctions

  println("memberFunctions")

  for (function in functions2) {

    println("${function.name}: ${function.returnType} .. ")

  }

 

  // 클래스에 선언된 확장 함수 반환

  val functions3 = arg.declaredMemberExtensionFunctions

  println("declaredMemberExtensionFunctions")

  for (function in functions3) {

    println("${function.name}: ${function.returnType} .. ")

  }

}

 

someFun(MyClass::class)

 

// declaredFunctions

// myFun: kotlin.Unit

// memberFunctions

// myFun: kotlin.Unit

// equals: kotlin.Boolean

// hashCode: kotlin.Int

// superFun: kotlin.Unit

// toString: kotlin.String

// declaredMemberExtensionFunctions

// someFun: kotlin.Unit

 

6. 함수 레퍼런스 분석

 

// 함수 레퍼런스

fun myFun() {}

 

class MyClass {

  fun myFun2() {}

}

 

val funReference: KFunction<*> = ::myFun

val funReference2: KFunction<*> = MyClass::myFun2

 

위와 같이 함수 레퍼런스를 KFuntion 타입으로 표현하면 KFunction 프로퍼티를 이용해 함수의 다양한 정보(name(함수 이름), parameters(매개변수), returnType(반환 타입))를 추출할 수 있습니다

 

// 고차 함수 이용

fun myFun(no: Int): Boolean {

  return no > 10

}

 

val array = arrayOf<Int>(10, 5, 30, 15)

array.filter(::myFun).forEach{ println(it) }

// 30

// 15

 

7. 프로퍼티 레퍼런스 분석

 

프로퍼티 레퍼런스는 두 가지 타입으로 표현하는데 KProperty<*>와 KMutableProperty<*>입니다. val로 선언한 프로퍼티는 KProperty, var로 선언한 프로퍼티는 KMutableProperty로 표현합니다

 

// 프로퍼티 정보 분석

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())

  }

}

 

reflectionProperty(null, ::myVal)

reflectionMutableProperty(null, ::myVar)

 

val obj: MyClass = MyClass()

reflectionProperty(objMyClass::objVal)

reflectionMutableProperty(objMyClass::objVar)

 

// property name: myVal, property type: kotlin.Int

// 3

// property name: myVar, property type: kotlin.Int

// 40

// property name: objVal, property type: kotlin.Int

// 10

// property name: objVar, property type: kotlin.Int

// 40

[출처] [Kotlin] 코틀린의 리플렉션(Reflection)|작성자 조원호의 행복한 인생

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함