공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
이번 포스팅에서는 Inline Function 에 대해 공부합니다.
Inline Functions
고차 함수를 사용하게 되면 런타임 시 특정 패널티가 부과됩니다.
각 메서드는 객체이고, 메서드의 body 에서 엑세스 되는 변수인 closure를 캡처 합니다.
메모리 할당과 가상 호출은 런타임 오버 헤드를 초래합니다.
하지만, 많은 경우에 이런 오버 헤드는 람다 식을 inlining 함으로써 제거될 수 있습니다.
아래의 함수가 이러한 상황의 좋은 예입니다.
lock(l) { foo() }
매개 변수에 대한 함수 객체를 만들고 호출을 생성하는 대신
컴파일러에서 다음 코드를 생성 할 수 있습니다.
l.lock() try { foo() } finally { l.unlock() }
컴파일러가 작업을 수행하게 하려면 lock() 메서드를 inline 수정자로 표시해야 합니다.
inline fun <T> lock(lock: Lock, body: () -> T): T { // ... }
인라인 수정자는 메서드 자체와 전달된 람다에 모두 영향을 미칩니다.
noinline
인라인 메서드에 전달된 일부 람다만 인라인되게 하려면 noinline 수정자를 사용하면
됩니다.
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { // ... }
inlinable 람다는 오직 인라인 메서드 내에서만 호출되거나, 인라인 가능 인자로
전달될 수 있지만, noinline 은 우리가 선호하는 어떤 방식으로도 조작 가능합니다.
* 인라인 메서드에 인라인 메서드 파라미터가 없고, 구체화된 타입의 파라미터가 없는
경우 컴파일러에서 해당 메서드를 인라인하면 매우 유용하지 않기 때문에
컴파일러에서 경고를 표시합니다.
Non-local returns
코틀린에서는 명명되지 않은 메서드나, 익명 함수를 종료하기 위해
'return' 만 사용할 수 있습니다.
즉, 람다를 빠져나오려면 label을 사용해야 하고, 람다가 둘러싼 메서드를 반환할 수
없기 때문에 'return'을 금지합니다.
fun foo() {
ordinaryFunction {
return // ERROR: can not make `foo` return here
}}
하지만, 람다가 전달된 메서드가 인라인되면, 리턴도 인라인될 수 있으므로 허용됩니다.
fun foo() {
inlineFunction {
return // OK: the lambda is inlined
}
}
이러한 반환(람다에 위치해 있지만, 포함 메서드를 빠져나옴)을
non-local returns 라고 부릅니다.
우리는 인라인 메서드가 자주 묶는 아래와 같은 메서드에 익숙합니다.
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true
}
return false
}
일부 인라인 메서드는 파라미터로 전달된 람다를 body에서 직접 호출하지 않고,
로컬 오브젝트나, 중첩 메서드와 같은 실행 컨텍스트에서 호출할 수 있습니다.
이러한 경우에는 non-local control flow는 람다에서 허용되지 않습니다.
이것을 나타내기 위해서 람다 파라미터를 crossinline 수정자를 표시해야 합니다.
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ...
}
인라인 람다에서 아직까지는 break 와 continue는 허용되지 않지만,
지원하려고 계획 중에 있다고 합니다.
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
Reified type paramaters : 구체화된 타입 파라미터
때때로 전달된 타입을 매개 변수로 접근해야 합니다.
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
treeNode.findParentOfType(MyTreeNode::class.java)
위처럼 사용하는데요, 별로 코드가 이쁘지 않습니다.
우리가 실제로 원하는 것은 아래 호출처럼 메서드에 유형을 전달하는 것입니다.
treeNode.findParentOfType<MyTreeNode>()
이 것을 가능하게 하기 위한 인라인 메서드는 구체화된 타입의 파라미터를 지원하기 때문에
아래처럼 작성할 수 있습니다.
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
reified 수정자로 타입 파라미터를 정규화했기 때문에 이제 메서드 내부에서
일반 클래스처럼 엑세스할 수 있습니다. 메서드가 인라인 되어있기 때문에
reflection 할 필요는 없습니다.
그리고 위에 언급한 것처럼 호출할 수 있습니다.
treeNode.findParentOfType<MyTreeNode>()
대부분의 경우 리플렉션이 필요하지 않지만, 구체화된 타입 파라미터로
리플렉션을 사용할 수 있습니다.
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
inline 마크가 없는 일반 메서드는 구체화된 파라미터를 가질 수 없습니다.
런타임 표현이 없는 타입은 구체화된 파라미터의 인수로 사용할 수 없습니다.
Inline properties(1.1버전부터)
인라인 수정자는 backing field 가 없는 프로퍼티의 접근자에서 사용할 수 있습니다.
ex)
val aaa: AAA
inline get() = AAA()
var bbb: BBB
get() = ...
inline set(v) { ... }
class AAA
class BBB
프로퍼티 앞에 inline 수정자를 붙여주면 속성 전체를 인라인으로 표시합니다.
inline var bbb: BBB
get() = ...
set(v) { ... }
호출 영역에서 인라인 접근자는 일반 인라인 메서드로 인라인됩니다.
이상입니다.
감사합니다.
다음 포스팅에서는
Destructuring Declarations
에 대해서 공부합니다.
'코틀린(Kotlin)' 카테고리의 다른 글
코틀린(kotlin) : Collections : List, Set, Map (0) | 2018.02.07 |
---|---|
코틀린(kotlin) : Destructuring Declaration (0) | 2018.02.06 |
코틀린(kotlin) : Lambda 고차함수와 람다, 익명 함수 (0) | 2018.02.06 |
코틀린(kotlin) : 메서드 Function (0) | 2018.02.05 |
코틀린(kotlin) : 객체 표현식과 객체 선언 Object Expression and Declaration (2) | 2018.02.05 |