Using higher-order functions imposes certain runtime penalties: each function is an object, and it captures a closure. A closure means those variables that are accessed in the body of the function. Memory allocations (both for function objects and classes) and virtual calls introduce runtime overhead.
But it appears that in many cases this kind of overhead can be eliminated by inlining the lambda expressions. The functions shown below are good examples of this situation. The
lock() function could be easily inlined at call-sites. Consider the following case:
Instead of creating a function object for the parameter and generating a call, the compiler could emit the following code:
To make the compiler do this, you need to mark the
lock() function with the
inline modifier affects both the function itself and the lambdas passed to it: all of those will be inlined into the call site.
Inlining may cause the generated code to grow; however, if you do it in a reasonable way (avoiding inlining large functions), it will pay off in performance, especially at "megamorphic" call-sites inside loops.
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
Inlinable lambdas can only be called inside the inline functions or passed as inlinable arguments, but
noinline ones can be manipulated in any way you like such as stored in fields or passed around.
In Kotlin, you can only use a normal, unqualified
return to exit a named function or an anonymous function. To exit a lambda, use a label. A bare
return is forbidden inside a lambda because a lambda cannot make the enclosing function
But if the function the lambda is passed to is inlined, the return can be inlined as well. So it is allowed:
Such returns (located in a lambda, but exiting the enclosing function) are called non-local returns. This sort of construct in loops, which inline functions often enclose:
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
Reified type parameters
Sometimes you need to access a type passed as a parameter:
Here, you 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:
What you actually want is simply pass a type to this function. You can call it like this:
To enable this, inline functions support reified type parameters, so you can write something like this:
You qualified the type parameter with the
reified modifier to make it accessible inside the function, almost as if it were a normal class. Since the function is inlined, no reflection is needed, normal operators like
as are working now. Also, you can call it as mentioned above:
Though reflection may not be needed in many cases, you can still use it with a reified type parameter:
Normal functions (not marked as inline) cannot have reified parameters. A type that does not have a run-time representation (for example, a non-reified type parameter or a fictitious type like
Nothing) cannot be used as an argument for a reified type parameter.
inline modifier can be used on accessors of properties that don't have a backing field. You can annotate individual property accessors:
You can also annotate an entire property, which marks both of its accessors as
At the call site, inline accessors are inlined as regular inline functions.
Restrictions for public API inline functions
When an inline function is
protected and is not a part of a
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.
internal declarations and their parts, in their bodies.
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.