I had thought lambda was syntactic sugar for anonymous inner classes, but accidentally decomcompiled APK and found:

There is a difference between Kotlin’s lambda and Java’s anonymous inner classes.

Thus I write this blog post as a record of a trample pit.

First the conclusion:

  • Java’s anonymous inner classes capture references to external classes in the generated class, whether or not they need to capture external instances

  • Kotlin’s lambda is

    • For capturing a lambda expression, each time a lambda is passed as an argument, a new function instance is created, which is garbage collected after execution.

    • For non-capturing lambda expressions (pure functions), a singleton function instance is created for later reuse.

(When a Lambda expression accesses a non-static variable or object defined outside the expression, the Lambda expression is called “captured.”)

Ps: The following image is the Smali file decompiled by APK. Smali is the disassembly language for Android virtual machines. For performance reasons, the Android platform does not use a standard JVM, but a dedicated Anroid virtual machine (Dalvik up to 5.0, ART up to 5.0). The executable files of the Android VIRTUAL machine are not ordinary class files, but repackaged dex files. The dex file is decompiled into Smali code, so the Smali syntax is the disassembly language of the Android VIRTUAL machine.

Without further ado, let’s actually write a lambda and decompile anonymous inner classes to see what the inside looks like, okay

Example 1: lambda in Kotlin:

We write a lambda in Kotlin language

In conclusion, since the caller code in the example uses a form that does not capture a lambda expression (calling thread. sleep and no external instance of the Activity is required), lambda is compiled as a singleton rather than an inner class.

You can see that line 12 is actually compiled into three instructions:

First of all, we know sget – object is used to obtain the static variables, used here sget – object instruction Lcom/example/kotlinlambdatest/MainActivity $onCreate $1 of this class INSTANCE singleton, Store it in register V0;

This shows that in actual execution, we do not construct an object of the class of the lambda, but use a singleton form.

The check-cast instruction is then used to convert objects in the V0 register to type Runnable

Finally, the invoke-virtual directive calls the test() method of the class

Example 2: Anonymous inner class in Kotlin

If not, instances of the external class are not captured:

  • In the following example, in the onCreate() method, a call to the test() method passes in an anonymous inner class that implements the Runnable interface. The second image shows the generated smALI.

You can see the difference between the anonymous inner class in Kotlin and the lambda above:

First of all, call new – instance, constructed Lcom/exmple kotlinlambdatest/MainActivity $onCreate $1 instance;

Then, the class’s <init> method, the constructor, is called;

Then, the check-cast directive is called to transform to Runnable;

Finally, the invoke-virtual directive calls the Activity’s test method;

Example 3: Lambda in Java:

You can see lambda in Java is:

. First of all, the local instruction, specifies the local stores where v0 register Lcom/example/kotlinlambdatest/JavaLambdaTest such instances;

Then, sget-object means that the v1 register holds a generated INSTANCE singleton

Finally, invoke-virtual calls the test method of the class;

Example 4: Anonymous inner classes in Java

An instance of the external class is captured anyway:

  • In the following example, in the onCreate() method, a call to the test() method passes in an anonymous inner class that implements the Runnable interface. The second image shows the generated smALI. You can see that line 33 passes in the

    () method that calls the SecondActivity$1 class, passing in an instance of the external class

I tested it with the Android Profiler, and there was a memory leak when I fell back from SecondActivity to MainActivity:

conclusion

This blog post lists the differences between Kotlin’s lambda, Kotlin’s anonymous inner class, Java’s lambda, and Java’s anonymous inner class.

  • Kotlin’s lambda, like Java’s lambda, actually generates a singleton if you don’t need to capture an instance of an external class;

  • For Kotlin’s anonymous inner class, if you don’t need to capture an instance of an external class, the constructor is not passed in when the object is generated.

  • Java’s anonymous inner class calls the constructor to pass in an instance of an external class whenever an object is generated, regardless of whether it needs to be captured.