Kotlin has a bunch of new concepts for folks like me approaching it from a Java background. Among these is the inline
keyword which can admittedly be pretty confusing if you don't already know what "inline" means in the context of a programming language.
What is inlining?
To "inline" a function basically means to copy a function's body and paste it at the function's call site. This happens at compile time.
When is this useful?
The inline
keyword is useful for functions that accept other functions, or lambdas, as arguments.
Without the inline
keyword on a function, that function's lambda argument gets converted at compile time to an instance of a Function
interface with a single method called invoke()
, and the code in the lambda is executed by calling invoke()
on that Function
instance inside the function body.
With the inline
keyword on a function, that compile time conversion never happens. Instead, the body of the inline
function gets inserted at its call site and its code is executed without the overhead of creating a function instance.
Hmm? Example please.
Let's say we have a function in an activity router class to start an activity and apply some extras (this is an Android blog after all).
fun startActivity(context: Context,
activity: Class<*>,
applyExtras: (intent: Intent) -> Unit) {
val intent = Intent(context, activity)
applyExtras(intent)
context.startActivity(intent)
}
This function creates an intent, applies some extras by calling the applyExtras
function argument, and starts the activity.
If we look at the compiled bytecode and decompile it to Java, this looks something like:
void startActivity(Context context,
Class activity,
Function1 applyExtras) {
Intent intent = new Intent(context, activity);
applyExtras.invoke(intent);
context.startActivity(intent);
}
Let's say we call this from a click listener in an activity:
override fun onClick(v: View) {
router.startActivity(this, SomeActivity::class.java) { intent ->
intent.putExtra("key1", "value1")
intent.putExtra("key2", 5)
}
}
The decompiled bytecode for this click listener would then look like something like this:
@Override void onClick(View v) {
router.startActivity(this, SomeActivity.class, new Function1() {
@Override void invoke(Intent intent) {
intent.putExtra("key1", "value1");
intent.putExtra("key2", 5);
}
}
}
A new instance of Function1
gets created every time the click listener is triggered. This works fine, but it's not ideal!
Now let's just add inline
to our activity router method:
inline fun startActivity(context: Context,
activity: Class<*>,
applyExtras: (intent: Intent) -> Unit) {
val intent = Intent(context, activity)
applyExtras(intent)
context.startActivity(intent)
}
Without changing our click listener code at all, we're now able to avoid the creation of that Function1
instance. The Java equivalent of the click listener code would now look something like:
@Override void onClick(View v) {
Intent intent = new Intent(context, SomeActivity.class);
intent.putExtra("key1", "value1");
intent.putExtra("key2", 5);
context.startActivity(intent);
}
That's it!
Inline functions are not super complicated and can make our code more efficient without much extra work for us.
Of course, this is just the tip of the iceberg! Check out the Kotlin docs for more on inline functions.