You never touched Groovy, nor did you jump on the Scala train. Clojure never attracted you; and you heard about Ceylon long after the language had already died. You are one of those old-fashioned Java folks! But now, after all those years, you want to join the cool Kotlin kids. So, where to start? Let’s discover the language together by decompiling it to Java code. Today: Functions!

Today: Functions

When you think about programming language, the first thing you consider is it uses of variables and functions. Though Kotlin is an object-oriented like Java, it dabbles in the functional programming world as well. So let’s start with that. As Kotlin functions can be declared at the top level in a file, it’s as easy as creating a "Test.kt" file and merely write:

fun add(a: Int, b: Int): Int {
  return a + b
}

If a function consists of one line, i.e. a 'single expression', you could even omit the return keyword and the return type:

fun add(a: Int, b: Int) = a + b

Now, let’s see the compiled code, shall we:

public final class TestKt {
  public static final int add(int a, int b) {
    return a + b;
  }
}

That was quite easy, wasn’t it? Kotlin let us do more awesome stuff. What about nesting one function in another?

fun addPlusTwo(a: Int, b: Int): Int {
  fun addTwo(a: Int) = a + 2
  return addTwo(a + b)
}

Short, concise and fun. Just as we like it! The compiled Java code on the other hand…​ That’s different![1]:

final class TestKt$addPlusTwo$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function1<Integer> {
  public Integer invoke(Integer a) {
    return a + 2;
  }
}

public final class TestKt {
  public static final int addWithNestedFunction(int a, int b) {
    var $fun$addTwo$1 = new TestKt$addPlusTwo$1();
    return $fun$addTwo$1.invoke(a + b);
  }
}

As you can see, nested functions do not really exist at JVM level. The compiler simply creates a new class to meet the need! This additional class implements a Function1 interface, which contains an invoke method where the actual calculation is performed.

To make this work for more than one argument, Kotlin provides Function0 till Function22 classes at runtime[2]. Each function takes the number of arguments as input and returns one result. So Function2 would take two inputs and return one output. Naively thinking, coming from a Java backend, you could imagine yourself writing a higher-ordered function with these classes:

fun addWithCustomImpl(a: Int, b: Int, add: Function2<Int, Int, Int>) = add(a, b)

Luckily enough, Kotlin provides syntactic sugar to write above code a little bit nicer:

fun addWithCustomImpl(a: Int, b: Int, add: (Int, Int) -> Int) = add(a, b)

However, both examples do compile! Looking at the compiled source code, I think you can imagine why:

public static final int addWithCustomImpl(int a, int b, @NotNull Function2 add) {
  Intrinsics.checkNotNullParameter(add, "add");
  return ((Number)add.invoke(a, b)).intValue();
}

What’s funny though, there is no extra class needed in this situation. Since the Function2 is just a regular Java functional interface, the compiler uses it as such!

There is one more thing that requires special attention. How many times have you used an API that was just missing that one method you needed? Kotlin offers the ability to add functions to existing objects, called extension functions:

fun String.isYesOrNo() = this.equals("yes", ignoreCase = true)
val example = "YES".isYesOrNo() // usage

Does Kotlin rewrite objects to make this work? Well look for yourself:

public static final boolean isYesOrNo(@NotNull String $this$isYesOrNo) {
  Intrinsics.checkNotNullParameter($this$isYesOrNo, "$this$isYesOrNo");
  return StringsKt.equals($this$isYesOrNo, "yes", true);
}

private static final boolean example = isYesOrNo("YES"); // usage

Nope, there is no magic involved at all…​ The compiler creates a normal static function and calls it. So extension functions are nothing more than syntactic sugar!

Well, that’s enough for one day. Stay tuned for more!


1. Actually the compiled code is even harder to read, I simplified it a little.
2. And FunctionN for functions with more than 23 arguments. Also notice Function1 and Function2 are very similar to Java’s Function and BiFunction interfaces!
shadow-left