« Higher Order functions in Kotlin under the hood

Let’s look at a simple function with a functional type parameter in Kotlin. For simplicity, I have created this method which accepts two parameters:

eventName of Type String

completionListener of Type function which returns Unit

1fun insertEvent(eventName: String, completionListener: () -> Unit) {
2 Log.i("anish", eventName)
3 completionListener.invoke()
4}

Let's convert this to Kotlin byte code and decompile it to the Java version.

Tools -> Kotlin -> Show Kotlin Bytecode and themn click on Decompile button.

kotlin_byte_code.png

1public static final void insertEvent(@NotNull String eventName, @NotNull Function0 completionListener) {
2 Intrinsics.checkNotNullParameter(eventName, "eventName");
3 Intrinsics.checkNotNullParameter(completionListener, "completionListener");
4 Log.i("anish", eventName);
5 completionListener.invoke();
6}

Let's break down each parameter:

eventName - the data type is still a String and it's annotated with @NonNull annotation in java — this param looks straightforward.

completionListener — the data type looks weird its called Function0 ??

What is Function0 if you do a control-click on Function0 it will navigate to Functions.kt

1package kotlin.jvm.functions
2
3/** A function that takes 0 arguments. */
4public interface Function0<out R> : Function<R> {
5 /** Invokes the function. */
6 public operator fun invoke(): R
7}
8/** A function that takes 1 argument. */
9public interface Function1<in P1, out R> : Function<R> {
10 /** Invokes the function with the specified argument. */
11 public operator fun invoke(p1: P1): R
12}
13/** A function that takes 2 arguments. */
14public interface Function2<in P1, in P2, out R> : Function<R> {
15 /** Invokes the function with the specified arguments. */
16 public operator fun invoke(p1: P1, p2: P2): R
17}
18/** A function that takes 3 arguments. */
19public interface Function3<in P1, in P2, in P3, out R> : Function<R> {
20 /** Invokes the function with the specified arguments. */
21 public operator fun invoke(p1: P1, p2: P2, p3: P3): R
22}
23.
24.
25.

Function0 is nothing but an interface with invoke method that has 0 params.

Function1 is another interface with invoke method that has 1 param

Function2 is another interface with invoke method that has 2 params and it goes on up until Function22

So under that hood, our higher-order function completionListener has been replaced by Function0 by Kotlin. If you know generics a little you should be able to picture what P1, P2, … and R are in Functions.kt

P1, P2, … are the generic types for the parameters of completionListener

R is the return generic type for the return type of the function in our case completionListener’s return type.

Ok, let's alter completionListener and add a param:

1// removed the eventName param
2fun insertEvent(completionListener: (test: String) -> Unit) {
3 completionListener.invoke("success")
4}

This is the generated code:

1public final void insertEvent(@NotNull Function1 completionListener) {
2 Intrinsics.checkNotNullParameter(completionListener, "completionListener");
3 completionListener.invoke("success");
4}

Function0 is now replaced with Function1 to support the String param.

Ok, will Kotlin support any number of parameters in our higher-order functions? I got this question as I noticed something weird in Functions.kt, there are Function interfaces up to Function22. What will happen if I have more than 22 parameters? Will Kotlin throw a compile error? Let's see...

Let's alter our completionListener to have 23 parameters.

1// I have removed the string param and replaced invoke method with a println.
2fun insertEvent(
3 completionListener: (
4 test1: String, test2: String, test3: String, test4: String, test5: String, test6: String, test7: String, test8: String, test9: String, test10: String, test11: String, test12: String, test13: String, test14: String, test15: String, test16: String, test17: String, test18: String, test19: String, test20: String, test21: String, test22: String, test23: String,
5 ) -> Unit
6) {
7 println("Just Printing dont want to invoke it :sweat:")
8}

Lets decompile

1public final void insertEvent(@NotNull FunctionN completionListener) {
2 Intrinsics.checkNotNullParameter(completionListener, "completionListener");
3 String var2 = "Just Printing dont want to invoke it :sweat:";
4 System.out.println(var2);
5}

Wow, now there is something called FunctionN. I’ll let you explore what is that.