Skip to content

Intermediate · Lambdas, streams and files · topic 36 of 60 · 25 min

Lambdas and functional interfaces

Explanation

A lambda is a short, anonymous function: (a, b) -> a + b. It can be used wherever a functional interface (an interface with exactly one abstract method) is expected, such as Comparator, Runnable, Predicate, Function, Consumer and Supplier. Lambdas can read local variables only if those are effectively final.

Why lambdas?

Before Java 8, passing a small piece of behaviour (how to compare, what to do when a button is clicked) needed an anonymous inner class of five or six lines. Lambdas say the same thing in one line, which made the Stream API and modern Java style possible.

java
// Before Java 8: an anonymous class
Comparator<String> byLength = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return Integer.compare(a.length(), b.length());
    }
};

// Java 8+: a lambda
Comparator<String> byLength2 = (a, b) -> Integer.compare(a.length(), b.length());

Lambda syntax

java
Runnable hello = () -> System.out.println("Namaste");           // no parameters
Function<Integer, Integer> square = x -> x * x;                  // one parameter: brackets optional
BinaryOperator<Integer> add = (a, b) -> a + b;                   // two parameters
Function<Double, Double> withGst = amount -> {                   // a block body needs return
    double tax = amount * 0.18;
    return amount + tax;
};

The standard functional interfaces (java.util.function)

InterfaceMethodTakes → returnsExample
Predicate<T>testT → booleano -> o.amount() > 500
Function<T, R>applyT → Rs -> s.length()
Consumer<T>acceptT → nothingmsg -> System.out.println(msg)
Supplier<T>getnothing → T() -> LocalDate.now()
UnaryOperator<T>applyT → Ts -> s.trim()
BinaryOperator<T>apply(T, T) → T(a, b) -> a + b
BiFunction<T, U, R>apply(T, U) → R(price, qty) -> price * qty
java
Predicate<String> isValidPin = pin -> pin.matches("[1-9][0-9]{5}");
System.out.println(isValidPin.test("560001"));     // true
Predicate<String> notBlank = s -> !s.isBlank();
Predicate<String> both = isValidPin.and(notBlank);  // predicates combine with and, or, negate
Function<Integer, Integer> plusGst = p -> p + p * 18 / 100;
Function<Integer, String> label = p -> "Rs " + p;
System.out.println(plusGst.andThen(label).apply(100));   // Rs 118

Capturing variables: effectively final

A lambda can use local variables from the surrounding method, but only if they are final or effectively final (never reassigned after initialisation). This avoids confusing bugs when a lambda runs later or on another thread.

java
double rate = 18;
Function<Double, Double> tax = amt -> amt * rate / 100;   // OK: rate is never changed
// rate = 5;                                              // would make the lambda above a compile error

int count = 0;
// list.forEach(x -> count++);   // compile error: count must be effectively final
// Use a stream count(), or an AtomicInteger, instead.
Careful: Long lambdas with many lines hide logic and make stack traces harder to read. If a lambda needs more than two or three lines, move the code into a named method and pass a method reference (next topic).
Think of it like this: A lambda is like a short instruction slip you hand to a helper: "if the parcel is over 5 kg, put it on the left". The helper (a method like filter or sort) decides when to read the slip; you only write what to do.
Tip: You can write your own functional interface (annotated @FunctionalInterface) when a domain name helps, such as DiscountRule, but first check whether a standard one from java.util.function already fits.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Lambdas and functional interfaces · 1/6

Lambdas

In this lesson you will write lambdas, short functions you can pass around.

Prefer a video by a teacher? Find free videos on “java lambda expressions functional interfaces predicate function consumer supplier” on YouTube ↗

In real life: Configurable discount rules

A retail billing system applies different discount rules on different days: festival 10% off, member ₹50 off above ₹500, and so on. Each rule is a UnaryOperator<Double>, and today's rules are chained with andThen, so marketing can change rules without new if/else code.

java
UnaryOperator<Double> festival = p -> p * 0.9;
UnaryOperator<Double> member = p -> p > 500 ? p - 50 : p;
Function<Double, Double> today = festival.andThen(member);
System.out.println(today.apply(1000.0));   // 1000 → 900 → 850.0

Practice (0/3 done)

Task 1 · Question

Which functional interface fits o -> o.amount() > 500?

Task 2 · Question

Why does this NOT compile? int total = 0; List.of(1, 2, 3).forEach(n -> total += n);

Task 3 · Practice

Write three Predicate<String> rules for a username: at least 4 characters, only letters and digits, and not starting with a digit. Combine them with and(...) into one isValid predicate and test it on five usernames, printing each result.

Quick quiz

Question 1

1.What is a functional interface?

Question 2

2.Supplier<T> takes…

Question 3

3.Function<Integer, Integer> f = x -> x + 1; f.andThen(x -> x * 2).apply(3) is…

Question 4

4.When did lambdas arrive in Java?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is a functional interface?

Answer: An interface with exactly one abstract method

Lambdas implement functional interfaces.

Supplier<T> takes…

Answer: Nothing and returns a T

Supplier.get() produces a value.

Function<Integer, Integer> f = x -> x + 1; f.andThen(x -> x * 2).apply(3) is…

Answer: 8

(3 + 1) * 2 = 8.

When did lambdas arrive in Java?

Answer: Java 8

Java 8 (2014) added lambdas and streams.

What is a functional interface?Related

Answer: An interface with exactly one abstract method

It can be implemented by a lambda.

From Interfaces

Which call uses trailing lambda syntax?Related

Answer: list.filter { it > 0 }

The lambda moves outside the parentheses.

From Lambdas and higher-order functions

Predicate<int> p = n => n > 10; What is p(7)?Related

Answer: false

7 > 10 is false.

From Delegates, lambdas, Func, Action and Predicate

What makes a lambda "generic"?Related

Answer: It uses auto in its parameter list

auto parameters turn the call operator into a template.

From Lambdas: small functions written in place

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Lambdas, streams and files” before its exam: Method references, Streams: filter, map and collect, Collectors: grouping and summarising, Reading and writing files.
  2. Module 8 exam: Lambdas, streams and files · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Multithreading and concurrency. 60 topics left in the course before the final exam.

← Previous: Queues, stacks and priority queues