코틀린(Kotlin)

코틀린(kotlin) : Reflection 반사?, 코틀린에서 Reflection이란?

알통몬_ 2018. 2. 11. 11:00
반응형


공감 및 댓글은 포스팅 하는데

 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 

도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 

만들어 나가겠습니다^^

 


이번 포스팅에서는 Reflection 에 대해 공부합니다.

Reflection : 리플렉션은 런타임 시 자신의 프로그램 구조를 조사할 수 있도록 허용하는

언어와 라이브러리 기능의 집합입니다. 코틀린은 언어에서 함수와 프로퍼티를 일급 시민으로

만들고, 런타임에 함수나 프로퍼티의 타입 또는 이름을 학습하는 것은 단순하게 함수적 스타일

또는 반응적 스타일을 사용하는 것과 밀접하게 연관되어 있습니다.


* 자바 플랫폼에서 Reflection 기능을 사용하는데 필요한 구성 요소는 kotlin-reflect.jar 파일로

배포됩니다. 따로 배포하는 이유는 Reflection 을 사용하지 않는 애플리케이션에서 필요한

런타임 라이브러리의 크기를 줄이기 위함입니다. 만약 Reflection을 사용해야 한다면, .jar 파일을

프로젝트 classpath에 경로에 추가해주시면 됩니다.


Class References 클래스 참조

가장 일반적인 Reflection 기능은 런타임 참조를 클래스로 가져오는 것입니다.

static 클래스에 대한 참조를 얻으려면 아래 구문처럼 사용하면 됩니다.

val myCalss = MyClass::class

참조는 KClass 타입의 값입니다.

* 코틀린 클래스 참조는 자바 클랙스 참조와는 다릅니다.

자바 클래스의 참조를 얻으려면 KClass 인스턴스에 .java 프러퍼티를 사용해야 합니다.


Bound Class References ( 1.1버전부터)

object를 receiver로 사용하는 동일한 ::class 구문을 사용하여 특정 object의 클래스에 대한

참조를 가져올 수도 있습니다.

fun main(args: Array<String>) {
val myClass = MyClass()
assert(myClass is MyClass) {"Bad myClass: ${myClass::class.qualifiedName}"}
}
class MyClass

receiver 의 expression 타입에도 불구하고, MyClass, YourClass 에 대한 정확한

object 참조를 얻을 수 있습니다.


Function References

아래와 같은 메서드를 선언했을 때

fun isEven(a : Int) = a % 2 == 0

그냥 직관적으로 isEven(10) 이렇게 사용할 수도 있지만, 아래처럼 사용할 수도 있습니다.

fun main(args: Array<String>) {

val nums = listOf(1,2,3,4,5)
print(nums.filter(::isEven))
}

:: 참조는 오버로딩된 메서드에도 사용할 수 있습니다.

예제)

fun main(args: Array<String>) {
val nums = listOf(1,2,3,4,5)
println(nums.filter(::isEven))
val strs = listOf("altongmon","antongmon","tongtongmon","tistory","tastory")
print(strs.filter(::isEven))
}

fun isEven(a : Int) = a % 2 == 0
fun isEven(str : String) = str == "altongmon" || str == "tistory"


또는, 명시적으로 지정된 타입의 변수에 메서드 참조를 지정해 필요한 컨텍스트를

제공할 수도 있습니다.

var wow : (String) -> Boolean = ::isEven


만약 클래스의 멤버나, 확장함수를 사용하고 싶을 경우에는 

아래처럼 필요 조건을 만족시켜야 합니다.

String::toCharArray는 String : String.() -> CharArray 타입에 대한 확장 함수를

제공합니다.


Example : Function Composition

아래와 같은 메서드가 있을 때

fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
return { x -> f(g(x)) }
}

이 메서드는 compose(f, g) = f(g(*))로 전달된 두 메서드의 조합을 반환합니다.

아래처럼 호출 할 수 있습니다.

fun main(args: Array<String>) {
val evenLength = compose(::isEven, ::length)
val strs = listOf("atongmon","antongmon","tongtmon","tistory","tastory")
print(strs.filter(evenLength))

}

fun isEven(a : Int) = a % 2 == 0
fun length(str : String) = str.length
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
return { x -> f(g(x)) }
}


Property References

코틀린에서 일급 객체 프로퍼티에 접근하려면, :: 연산자를 사용해 접근할 수 있습니다.

val a = 10

fun main(args: Array<String>) {

println(::a.get())
println(::a.name)

}

::a 는 프로퍼티의 obejct 타입이 KProperty<Int> 인지 먼저 평가하고, 맞으면

get()을 통해 해당 값을 읽어오고, name으로 프로퍼티 이름이 읽어 옵니다.

더 많은 정보 : https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property/index.html


Mutable Property var b = 10 의 경우 ::b 는 KMutableProperty<Int> 타입이 맞는지 결과를

반환합니다. 그리고 Mutable 이기 때문에 set() 도 사용 가능합니다.

var b = 100
fun main(args: Array<String>) {
::b.set(200)
print(b)

}


프로퍼티 참조는 파라미터가 없는 메서드가 예상되는 곳에서 사용할 수 있습니다.

val str = listOf("aaa","a","aaaa","aa","aaaaa")
println(str.map(String::length))


클래스의 멤버 프로퍼티에도 접근할 수 있습니다.

fun main(args: Array<String>) {

val a = AAA::a
print(a.get(AAA(10)))

}

class AAA(val a: Int)


확장 프로퍼티를 쓸 경우 :

val String.lastChar: Char
get() = this[length - 1]

fun main(args: Array<String>) {

println(String::lastChar.get("가나다라마바사"))
}


Java Reflection 과 상호 운용

자바 플랫폼에서는 표준 라이브러리는 Java Reflection object 와 매핑을 제공하는

Refelction class의 확장을 포함하고 있습니다.(package kotlin.reflect.jvm 참조)

예를 들어 backing field를 찾거나, 코틀린 프로퍼티에 대한 getter 역할을 하는

자바 메서드를 찾으려면 아래처럼 하면 됩니다.

import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter

open class AAA(var a : Int)

fun main(args: Array<String>) {

println(AAA::a.javaField)
print(AAA::a.javaGetter)
}


자바 클래스에 해당하는 코틀린 크래스를 가져오려면 .kotlin 확장 프로퍼티를

사용하면 됩니다.

fun getKClass(o : Any) : KClass<Any> = o.javaClass.kotlin


Constructor References

생성자는 메서드나 프로퍼티와 마찬가지로 참조될 수 있습니다.

생성자와 동일한 매개 변수를 사용하는 적절한 객체 타입을 반환하는 메서드 타입의 객체가

예상되는 모든 곳에서 사용할 수 있습니다. 생성자 참조는 :: 연산자와 클래스 이름을

사용합니다. 

아래 예제 코드처럼 사용하면 됩니다.

fun main(args: Array<String>) {
function(::AAA)
}

class AAA

fun function(factory : () -> AAA) {
val a : AAA = factory()
}


Bound Function and Property References(1.1버전부터)

특정 객체의 인스턴스를 참조할 수 있습니다.

ex)

fun main(args: Array<String>) {

val numberRegex = "\\d+".toRegex()
println(numberRegex.matches("29"))

val isNumber = numberRegex::matches
println(isNumber("10"))

}

matches 메서드를 직접 호출하는 대신에 참조를 저장하고 있습니다.

이러한 참조는 그 receiver에 묶여 있습니다. 

위 예제처럼 직접 호출하거나, 메서드 타입의 표현식이 필요할 때 사용할 수 있습니다.

fun main(args: Array<String>) {

val numberRegex = "\\d+".toRegex()
val strs = listOf("AAA","123","#@!")
println(strs.filter(numberRegex::matches)) //[123]
}


이상입니다.

감사합니다.


반응형