Kotlin is a great programming language and I guess all Android developers have already started adopting it in their code base. However, it has a learning curve and numerous language features, which Java does not have. In order to write bug-free code, we need to be extra careful when using these new features, simply because we might make mistakes due to our years of Java coding. The following example mistake might be an extreme corner case, but it still managed to slip through code review.
Kotlin inline functions introduction
Kotlin supports higher-order functions, but using them imposes certain runtime penalties: each function is an object, and it captures a closure, i.e. 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. Inline functions allow that the function call is effectively replaced by the compiler with the function body. This feature could help with performance improvements, and also can reduce your method count in your codebase, which is especially painful topic in Android development. For more information visit the official documentation for Kotlin Inline Functions.
So here is an example of a simple inline function:
This function simply prints some text to the console, invokes an input lambda expression and returns its result. Pretty simple and awesome.
The misplaced return statement
The above function is great, but it could cause trouble if you do not invoke it properly, due to its inline nature. Let me explain by giving an example.
So what exactly is the problem here? Well, the position of the return statement. You see, as Java developers, we’re used to writing return statement at the end of a function that returns a value. However, Kotlin will actually show you a compile time error if you try to write a return statements in a lambda.
But wait! Why does the example above even compile? Well, it’s due to the fact that the lambda is being passed to an inline function. So when we compile the code, what effectively will happen after the inline function gets replaced is analogous to the following:
The code above might not be exactly what Kotlin generates in byte code, but please bear with me, I’m just trying to make a point with it. You see, if we write the return statement at the wrong place we will effectively skip the remaining inline function body, which might be a serious bug and a very hard one to spot. The following example shows the correct way to invoke the lambda in an inline function. Notice the position of the return statement. It is not inside the lambda.
And here is the log in the terminal, showing clearly when the “Do work after” is invoked and when not.
How to not make this mistake
First, just for testing purposes, remove the inline keyword in front of the executeBetweenWork function. When you do this, the compiler will immediately spot the misplaced return statement and mark it as a compiler error. But please do not give up completely on inline functions! There is another way of fixing this issue. You can mark any of the input parameters with the modifier noinline like this:
When you do this, the compiler will again spot the misplaced return statement and mark it as a compile time error.
Summary
Inline functions and lambdas are great features of the Kotlin language. We should definitely learn how to used them and add them to our toolkit for developing apps every day. However, we should be mindful with them and be extra careful to keep our execution flow exactly the way we want it to be.