We know that in Android Java world, the lambda will be converted to anonymous class in the bytecode level. After Android start transferring into the Kotlin world, does Kotlin treat lambda expression the same way as Java? In this post, I will talk about how Kotlin implement and optimize lambda expression from a bytecode level. All the code based on Kotlin-1.4.10
How lambda implemented in bytecode level
First, let’s look at the following Kotlin lambda code snippet
the decompiled bytecode will be like
when we use lambda in Kotlin, an inner static anonymous class will be created under the hood to replace the lambda. And as an optimization, instead of creating a new instance every time we invoke the lambda, the same single instance will be reused for every invocation.
In this example, we can see that the lambda is converted to an anonymous inner static class named MainActivity$onCreate$1.class, and the same instance has been reused across invocations
If the lambda captures variables from the surrounding scope, it’s no longer possible to reuse the same instance for every invocation, see the following snippet
the decompiled bytecode will be like
Instead, a new inner anonymous class instance will be created for every invocation. And the captured variable will be copied to the instance by constructor.
Passing SAM interface to a Java method
If you want to call a method defined in Java that accept a SAM (single abstract method) interface as parameter, you can pass in lambda
or you can achieve the same effect by using object expression.
So, what’s the difference? Let’s take a look at the bytecode. First, below is the decompiled bytecode for the lambda version
the lambda is converted to a static inner anonymous class implementing the SAM interface, and if the lambda doesn’t access any variables from surrounding scope, the same instance of the anonymous class is reused between calls.
Let’s examine the bytecode for the object expression version
we can see that when using object expression, a new instance implementing SAM interface is created on each invocation.
So, we should prefer using the lambda version to reduce the instance creation
Note: If the lambda captures variables from the surrounding scope, it’s no longer possible to reuse the same instance for every invocation
Summary
In Kotlin
- If the lambda expression do not capture surrounding variable, the same anonymous class instance will be reused between calls
- If the lambda captures surrounding variable, a new anonymous class instance will be created for every invocation
- For passing SAM interface into Java method, prefer the lambda version to reduce instance creation
Thanks for reading!