Lambda Expressions Syntax - Java vs Kotlin

Both Java and Kotlin support lambda expressions. There are similarities in their syntax, though they are precisely not the same. Many of you may be using both languages at work. When you use multiple languages, the syntax of another language may peek in. In my example, after using Groovy for several years, omitting semicolon (;) was a common mistake while I was programming in Java.

I noticed that while programming with Kotlin, my lambda expression wouldn't compile several times. When I took a closer look at the code, I realised that I was using Java syntax instead.

Let us take a closer look at the differences between Java and Kotlin in their lambda expressions syntax. To start with, let's take a look at the following examples.

Java

Supplier<String> messageProvider = () -> "Hello";
Function<String, String> greet = person -> "Hello " + person;
BiFunction<Integer, Integer, Integer> add = (first, second) -> first + second;

Kotlin

val messageProvider = { "Hello" }
val greet: (String) -> String = { person -> "Hello $person" }
val add: (Int, Int) -> Int = { first, second -> first + second }

In the above code blocks, we have three lambda expressions written in both Java and Kotlin. Since our focus is the syntax of lambda expressions, let us focus on the right-hand side of the expressions (what follows '=' symbol).

The first lambda expression takes no parameters and returns a value. In Java, you had to put a pair of empty parentheses to indicate no parameters. In contrast, in Kotlin, you could have the expression (body), and we can even omit the '->' symbol since we don't have any parameters.

The second lambda expression takes one parameter and returns a value. In Java, parentheses are optional for a single parameter.

The third lambda expression declares two parameters and returns a value. In Java, when you have more than one parameters, enclosing them in parentheses is mandatory.

Note how in Kotlin the entire lambda expression is always enclosed within a pair of brace '{}' symbols.

Now let's take a look at lambdas with multiple statements/ expressions in their bodies. Let's achieve this by simply adding a print statement to the body of add.

Java

BiFunction<Integer, Integer, Integer> add = (first, second) -> {
    System.out.println("Adding");
    return first + second;
};

Kotlin

val add: (Int, Int) -> Int = { first, second ->
    println("Adding")
    first + second
}

In Java, we had to surround the body with the curly braces. Also, we had to add the return statement explicitly. In Kotlin, all we had to do was to add the print statement to the body.

When you move from Java to Kotlin, the curly braces surrounding the entire lambda expression seems like noise. However, this helps in making the experience of evolving the lambda expression very fluent. You can add another parameter or add statements to the body seamlessly.

An overview of the lambda expressions grammar in Java is as follows.

LambdaExpression:
  LambdaParameters -> LambdaBody

LambdaBody:
  Expression
  Block

For more details, refer https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27-110

An overview of the lambda expressions grammar in Kotlin is as follows.

lambdaLiteral
    : LCURL NL* statements NL* RCURL
    | LCURL NL* lambdaParameters? NL* ARROW NL* statements NL* RCURL
    ;

lambdaParameters
    : lambdaParameter (NL* COMMA NL* lambdaParameter)* (NL* COMMA)?
    ;

lambdaParameter
    : variableDeclaration
    | multiVariableDeclaration (NL* COLON NL* type)?
    ;

For a deeper understanding of the grammar refer to the following links

  1. https://github.com/Kotlin/kotlin-spec/blob/release/grammar/src/main/antlr/KotlinParser.g4
  2. https://github.com/Kotlin/kotlin-spec/blob/release/grammar/src/main/antlr/KotlinLexer.g4

Once I took the time to explore these details, the mistakes reduced drastically.