Skip to main content

Lambda Expressions

A Lambda Expression is a short block of code which takes in parameters and returns a value.

In Java, Lambdas are essentially shorthand syntax for creating an anonymous class that implements a Functional Interface. They allow you to treat functionality as a method argument, or code as data.


The Syntax

The syntax of a lambda expression consists of three parts:

  1. Parameters: A comma-separated list of formal parameters enclosed in parentheses.
  2. The Arrow Token: -> (often read as "becomes" or "goes to").
  3. The Body: The code block to be executed.
(parameters) -> { body }

Syntax Shortcuts

  • If there is only one parameter, you can drop the parentheses: param -> { body }
  • If the body is only a single line of code, you can drop the curly braces {} and the return keyword: (a, b) -> a + b;

Before vs After: The Runnable Interface

To understand how much boilerplate code Lambdas eliminate, let's look at the Runnable interface. Runnable is a classic Functional Interface because it only has one abstract method: void run().

Before Java 8: Anonymous Inner Classes

If you wanted to pass a custom Runnable to a Thread without creating a whole new file, you had to use an Anonymous Inner Class:

public class LabLambda1 {

public static void main(String[] args) {
// The massive boilerplate to define a single method!
Runnable myRunnable = new Runnable() {
@Override
public void run() {
System.out.println("Running in a separate thread!");
}
};

Thread t = new Thread(myRunnable);
t.start();
}
}

After Java 8: Lambda Expressions

Because the compiler knows that Thread expects a Runnable object, and it knows Runnable only has one method (run()), it can infer everything else!

public class LabLambda2 {

public static void main(String[] args) {
// The Lambda automatically provides the implementation for the run() method!
Runnable myRunnable = () ->
System.out.println("Running in a separate thread!");

Thread t = new Thread(myRunnable);
t.start();
}
}

Implementing Built-in Functional Interfaces

Let's look at how to use Lambda expressions to implement the core functional interfaces provided in java.util.function.

1. The Predicate (Returns boolean)

import java.util.function.Predicate;

public class LabLambda3 {

public static void main(String[] args) {
// A lambda that returns true if the string length is greater than 5
Predicate<String> isLongWord = (str) -> str.length() > 5;

System.out.println("Is 'Apple' long? " + isLongWord.test("Apple")); // false
System.out.println("Is 'Banana' long? " + isLongWord.test("Banana")); // true
}
}

2. The Consumer (Returns void)

import java.util.function.Consumer;

public class LabLambda4 {

public static void main(String[] args) {
// A lambda that just prints what it receives
Consumer<String> greeter = (name) ->
System.out.println("Hello, " + name + "!");

greeter.accept("Alice");
greeter.accept("Bob");
}
}

3. The Function (Transforms an object)

import java.util.function.Function;

public class LabLambda5 {

public static void main(String[] args) {
// A lambda that takes a String and returns its Integer length
Function<String, Integer> stringLengthFinder = (str) -> str.length();

int length = stringLengthFinder.apply("Docusaurus");
System.out.println("The length is: " + length); // 10
}
}

[!TIP] The absolute biggest use-case for Lambda expressions is passing them directly into Stream API operations, allowing you to filter, map, and process collections of data in a highly readable, declarative style!