How Kotlin lambda capture variable

yawei
2 min readOct 7, 2020

--

In Android Java world, you know that when you declare a lambda in a function, you can access the parameters of that function as well as the local variables declared before the lambda. These variables are said to be captured by the lambda, but the variables must be final or effectively final to be captured by lambda. In Kotlin, this constraint has been removed. In this post, I will explore why Java need the variables to be final to be captured by lambda and how Kotlin removed that constraint.

Why Java need the variables to be final to be captured by lambda

In Android Java, the lambda will be transfer to a synthesized class, see the following example:

the lambda captures the prefix variable in outer scope, the corresponding synthesized class will be like:

we can see that a synthetic field f$0 is generated for the captured variable and the captured variable is copied into f$0 via constructor. So that if we change captured variable value in the lambda, the enclosing scope variable will not be affected. It also causes confusion that some developers might expect those changes to be visible within the body of the enclosing method. So, making the captured variable final remove the possibility to change the value in lambda

How Kotlin removed the final variable constraint

In Kotlin, the lambda also will be translated to a synthesized class. If the captured variable is val

the equivalent decompiled bytecode will be like

we can see as the variable is readable, it’s still copied into a synthetic field of the synthesized class via constructor as the Java does

If the captured variable is var

the equivalent decompiled bytecode will be like

We can see that the String variable is wrapped into the ObjectRef class(it’s a Kotlin inner implementation class), then the wrapped class is being copied into the synthesized class, so that the change in lambda will be visible in the enclosing scope. The source code of ObjectRef is as follows:

Summary

In Java, if we want to change the captured variable value in the lambda, we must wrap the value into an array or a mutable wrapper type by ourselves. In Kotlin, the inconvenience has been removed and the Kotlin compiler has handle the boilerplate code for us, so that we can write more concise code.

Thanks for reading!

--

--