Shiklo Programming
EN
  • English
  • Español
  • Português
  • Deutsch
  • Français
  • 日本語
  • हिन्दी

Java Lambda Expressions

A lambda expression is a short way to write a small function and pass it around like a value. Instead of writing a whole class to say "compare two players by score" or "keep only the even numbers", you write just the parameters, an arrow and the code: (a, b) -> a.score - b.score. Lambdas make code that works with collections much shorter, and they are the basis of Streams.

Syntax

A lambda implements an interface that has exactly one abstract method, called a functional interface. You met one in Inner Classes, where an anonymous class implemented Discount. The lambda does the same in one line:

interface Discount {
    double apply(double price);
}

public class Main {
    public static void main(String[] args) {
        Discount festival = price -> price * 0.8;
        Discount flat = price -> price - 100;
        Discount none = price -> price;
        System.out.println(festival.apply(1000));
        System.out.println(flat.apply(1000));
        System.out.println(none.apply(1000));
    }
}
Output
800.0
900.0
1000.0
  • One parameter needs no brackets: price -> .... Zero or several parameters do: () -> ..., (a, b) -> ....
  • A single expression is returned automatically. For several statements, use curly brackets and return.

Ready-made functional interfaces

You rarely need your own interface: java.util.function has the common shapes. Predicate answers yes or no, Function turns a value into another, Consumer does something with a value, and Supplier produces one:

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Function<String, Integer> length = s -> s.length();
        Supplier<String> greeting = () -> "Namaste";
        System.out.println(isEven.test(10));
        System.out.println(length.apply("lambda"));
        System.out.println(greeting.get());
        Predicate<Integer> isOddAndBig = isEven.negate().and(n -> n > 100);
        System.out.println(isOddAndBig.test(101));
    }
}
Output
true
6
Namaste
true

Lambdas with collections

Many collection methods take a lambda. forEach runs it for every item, removeIf removes the items for which it returns true, and sort uses it to compare two items:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(List.of("Zoya", "Al", "Mohan", "Ira", "Bhavana"));
        names.removeIf(n -> n.length() < 3);
        names.sort((a, b) -> a.length() - b.length());
        names.forEach(n -> System.out.println(n + " (" + n.length() + ")"));
    }
}
Output
Ira (3)
Zoya (4)
Mohan (5)
Bhavana (7)

Comparator and method references

The Comparator interface has helper methods that build comparisons from a lambda. When a lambda only calls an existing method, a method reference like String::length or System.out::println says the same thing even more shortly:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

record Player(String name, int score) {
}

public class Main {
    public static void main(String[] args) {
        List<Player> players = new ArrayList<>(List.of(
                new Player("Ana", 40), new Player("Raj", 75), new Player("Lee", 75), new Player("Kim", 60)));
        players.sort(Comparator.comparing(Player::score).reversed().thenComparing(Player::name));
        players.forEach(System.out::println);
    }
}
Output
Player[name=Lee, score=75]
Player[name=Raj, score=75]
Player[name=Kim, score=60]
Player[name=Ana, score=40]

The players are sorted by score from high to low, and players with the same score by name. Player is a record, so Player::score refers to its accessor method.

Using outside variables

A lambda can use variables from the surrounding method, but only if they are never changed afterwards (they are "effectively final"). This keeps lambdas predictable:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        int limit = 50;
        List<Integer> marks = List.of(35, 72, 48, 90);
        marks.forEach(m -> System.out.println(m + (m >= limit ? " pass" : " fail")));
    }
}
Output
35 fail
72 pass
48 fail
90 pass

Practice exercises

  1. Write a Predicate<String> that is true for words starting with a capital letter and test it.
  2. Sort a list of words alphabetically ignoring case with String::compareToIgnoreCase.
  3. Remove all negative numbers from a list with removeIf and print the rest with forEach.