With Kotlin, functions are first-class citizens, meaning you can assign them to variables and pass them into functions as parameters. If you have a function that takes a higher order function as a parameter, you can use the function modifier inline to inline the higher order function straight into your function. This is basically just copying and pasting the code to the call site.
The performance benefit is typically minimal. You can also mark specific higher order functions to be not inlined with the noinline modifier. As you can see, if we have an inline function that takes a single noinlined higher order function, your IDE should warn you that there will be minimal performance gains (notice inline has a yellow underline).
This generally is only useful if your function takes multiple higher order functions and you don’t want them all to be inlined.
The other, more important, benefit of the inline function modifier is the ability to reify a generic type. With type erasure, if you have a class that is generically typed, like List<T>, the type of T is lost at runtime and you are unable to query it. So if you had a listOf<Int>(), at runtime you won’t be able to find out what that type is. Kotlin allows you to combine the inline and reified modifiers to make this possible. As you can see below, checking the type of a generically typed function causes a compilation error, as the type will have been erased at runtime.
So instead we mark the function as inline, and the generic type as reified. This allows us to query the type at runtime.
A thing to keep in mind when using inlined functions is how the return flow changes. Take the forEach function in the Kotlin stdlib. This is an inline function. Let’s take a look at the following code:
And I invoke the function with doSomethingWith(listOf(1, 2, 3, 4, 5)). Do you think “Will this print?” will actually print? The answer is no, because the forEach function is inlined, meaning the code is copied and the return statement now returns out of the parent, being our entire function. To get around this, you can use labels like so:
Now, “Will this print?” will actually print. You can also define your own labels for readability sake.
Next, let’s take a look at a really cool thing we can do with higher order functions – passing in extension functions. Let’s say we define the following function:
And when we call it, we get passed back a string and we can access properties and functions on that string, like so:
As you can see, we’re accessing ‘it’, being a string, and using functions and accessing properties on it. This is a pretty simple example, but you could imagine a scenario where the parameter you passed in was some other class which had properties which themselves were other classes. You don’t really want to have it. everywhere. So instead, rather than passing in a string to the handler, you can pass an extension on string, like so:
And now, when calling this function, because we’re basically creating an extension to String on the fly, we don’t need to use it everywhere.
For something like a String, this technique doesn’t really make sense to use as it actually makes your code more difficult to understand. But imagine a class like HttpHandler, which has request and response properties. Rather than doing it.response, it.request, you can just access response and request like that, leading to cleaner code.