Skip to content
PickNTech

Lambdas and Functional Interfaces

Pass behaviour as data with lambda expressions and method references.

Intermediate16 minUpdated 22 Sept 2026

What you’ll learn

  • What a functional interface is
  • Lambda syntax and method references
  • Predicate, Function, Consumer and Supplier
  • Effectively final variables

Behaviour as a value

Before Java 8, passing behaviour to a method meant writing an anonymous class:

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

With a lambda, that becomes:

names.sort((a, b) -> a.length() - b.length());

Same behaviour, a fraction of the noise.

Functional interfaces

A lambda can be used anywhere Java expects a functional interface — an interface with exactly one abstract method. Comparator, Runnable and the types in java.util.function are all functional interfaces.

InterfaceSignatureUse it for
Predicate<T>T -> booleanTests / filters
Function<T, R>T -> RTransformations
Consumer<T>T -> voidSide effects (print, save)
Supplier<T>() -> TLazy creation
Predicate<String> isLong = s -> s.length() > 5;
Function<String, Integer> length = s -> s.length();
Consumer<String> printer = s -> System.out.println(s);
Supplier<List<String>> fresh = () -> new ArrayList<>();

Lambda syntax

x -> x * 2                       // one parameter, expression body
(a, b) -> a + b                  // multiple parameters
() -> "hello"                    // no parameters
(String s) -> s.isBlank()        // explicit type
order -> {                       // block body needs return
    var tax = order.total() * 0.18;
    return order.total() + tax;
}

Method references

When a lambda only calls an existing method, use a method reference:

names.forEach(s -> System.out.println(s));
names.forEach(System.out::println);          // same thing
 
names.stream().map(s -> s.toUpperCase());
names.stream().map(String::toUpperCase);     // same thing

Effectively final

Lambdas can read local variables from the surrounding method only if those variables are never reassigned (effectively final). This prevents confusing bugs, especially with concurrent code.

⚡ Try it yourself

Write a method List<String> filter(List<String> input, Predicate<String> rule) and call it three ways: words starting with "S", words longer than 4 characters, and non-blank words.

Interview questions

QWhat is a functional interface? Can you create your own?+

An interface with exactly one abstract method. Yes — just declare one, ideally annotated with @FunctionalInterface so the compiler enforces the single-method rule.

Key takeaways

  • Lambdas are concise implementations of functional interfaces.
  • Learn the four core types: Predicate, Function, Consumer, Supplier.
  • Use method references (Class::method) when they read better.

Finished this lesson?

Mark it complete to move forward on your roadmap.