high-order function 을 쓰는것은 런타임에 부담을 부과할수 있다. 각각의 함수가 오브젝트이고 이것을 클로저로 캡쳐했을때 런타임에 부담을 줄수 있는 상황인 것이다. 예를들어 함수의 본체에서 접근하는 변수들이 있다고 가정해보자. 함수 object 과 클래스 모두에 대한 메모리 할당과 가상 콜은 런타임 오버해드를 야기할있다.

하지만 인라인 람다 익스프레션을 사용하면 대부분의 오버해드의 경우가 없어질수있다! (예이이!!) lock 함수는 call-sites로 인라인화 될수있다.(the lock function could be easily inlined at call-sites.)

lock(l) { foo() }

매개 변수를 위한 함수객체를 만들고 호출을 생성하는 대신에 컴파일러는 아래와 같이 코드를 생성한다.

l.lock()
try {
    foo()
}
finally {
  l.unlock()
}

마치 처음에 하려고 했던것과 같이 컴파일시에 코드가 생성되는것이다!
사실 컴파일러가 이렇게 생성하려면, 우리는 lock()함수를 inline access modifier 로 지정해주어한다.

inline fun <T> lock(lock: Lock, body: () -> T): T { ... }

 inline modifier는 함수 그자체와 그곳에 넘겨주는 람다식에도 영향을 준다: 이 모든것은 콜 사이트에 인라인 된다.

인라이닝은 생성되는 코드들을 증가시키게 될것이지만 합리적인 방법(작은 단위로 인라인 하면)으로 수행하면 성능이 특히 루프 내부의 “거시적 인”콜 사이트에서 사용성이 좋아질 것이다.

noinline

만약 In case you want only some of the lambdas passed to an inline function to be inlined, you can mark some of your function parameters with the noinline modifier:

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }

Inlinable lambdas can only be called inside the inline functions or passed as inlinable arguments, but noinline ones can be manipulated in any way we like: stored in fields, passed around etc.

Note that if an inline function has no inlinable function parameters and no reified type parameters, the compiler will issue a warning, since inlining such functions is very unlikely to be beneficial (you can suppress the warning if you are sure the inlining is needed using the annotation @Suppress("NOTHING_TO_INLINE")).

Non-local returns

In Kotlin, we can only use a normal, unqualified return to exit a named function or an anonymous function. This means that to exit a lambda, we have to use a label, and a bare return is forbidden inside a lambda, because a lambda cannot make the enclosing function return:

fun foo() {
   ordinaryFunction {
       return // ERROR: cannot make foo return here
   }
}

Target platform: JVMRunning on kotlin v. 1.3.60

하지만 람다인 함수를 인라인으로 넘기면, 리턴값 역시 인라인이 될수있다.

fun foo() {
    inlined {
       return // OK: the lamda is inlined
    }
}
        return // OK: the lambda is inlined
    }
}

Target platform: JVMRunning on kotlin v. 1.3.60

Such returns (located in a lambda, but exiting the enclosing function) are called non-local returns. We are used to this sort of construct in loops, which inline functions often enclose:

fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true // returns from hasZeros
    }
    return false
}

Note that some inline functions may call the lambdas passed to them as parameters not directly from the function body, but from another execution context, such as a local object or a nested function. In such cases, non-local control flow is also not allowed in the lambdas. To indicate that, the lambda parameter needs to be marked with the crossinline modifier:

inline fun f(crossinline body: () -> Unit) {
    val f = object: Runnable {
        override fun run() = body()
    }
    // ...
}

break and continue are not yet available in inlined lambdas, but we are planning to support them too.

Reified type parameters

Sometimes we need to access a type passed to us as a parameter:

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?
}

Here, we walk up a tree and use reflection to check if a node has a certain type. It’s all fine, but the call site is not very pretty:

treeNode.findParentOfType(MyTreeNode::class.java)

What we actually want is simply pass a type to this function, i.e. call it like this:

treeNode.findParentOfType<MyTreeNode>()

To enable this, inline functions support reified type parameters, so we can write something like this:

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}

We qualified the type parameter with the reified modifier, now it’s accessible inside the function, almost as if it were a normal class. Since the function is inlined, no reflection is needed, normal operators like !is and as are working now. Also, we can call it as mentioned above: myTree.findParentOfType<MyTreeNodeType>().

Though reflection may not be needed in many cases, we can still use it with a reified type parameter:

inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
    println(membersOf<StringBuilder>().joinToString("\n"))
}

Normal functions (not marked as inline) cannot have reified parameters. A type that does not have a run-time representation (e.g. a non-reified type parameter or a fictitious type like Nothing) cannot be used as an argument for a reified type parameter.

For a low-level description, see the spec document.

Inline properties (since 1.1)

The inline modifier can be used on accessors of properties that don’t have a backing field. You can annotate individual property accessors:

val foo: Foo
    inline get() = Foo()
var bar: Bar
    get() = ...
    inline set(v) { ... }

You can also annotate an entire property, which marks both of its accessors as inline:

inline var bar: Bar
    get() = ...
    set(v) { ... }

At the call site, inline accessors are inlined as regular inline functions.

Restrictions for public API inline functions

When an inline function is public or protected and is not a part of a private or internal declaration, it is considered a module‘s public API. It can be called in other modules and is inlined at such call sites as well.

This imposes certain risks of binary incompatibility caused by changes in the module that declares an inline function in case the calling module is not re-compiled after the change.

To eliminate the risk of such incompatibility being introduced by a change in non-public API of a module, the public API inline functions are not allowed to use non-public-API declarations, i.e. private and internal declarations and their parts, in their bodies.

An internal declaration can be annotated with @PublishedApi, which allows its use in public API inline functions. When an internal inline function is marked as @PublishedApi, its body is checked too, as if it were public.

1+