« 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.
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.functions23/** A function that takes 0 arguments. */4public interface Function0<out R> : Function<R> {5 /** Invokes the function. */6 public operator fun invoke(): R7}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): R12}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): R17}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): R22}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 param2fun 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 ) -> Unit6) {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.