Lambdas & Functional Interfaces
A lambda expression is a concise way to represent a functional interface (an interface with exactly one abstract method). Lambdas eliminate verbose anonymous class boilerplate and enable functional-style programming. They are the foundation of the entire Stream API.
Lambda Syntax & Functional Interfaces
import java.util.function.*;
import java.util.Comparator;
public class LambdaDemo {
public static void main(String[] args) {
// Lambda syntax variations:
// (params) -> expression (single expression, no return needed)
// (params) -> { statements; return x; } (block body)
// ── Predicate<T>: T → boolean ────────────────────────────
Predicate<String> isLong = s -> s.length() > 5;
Predicate<String> startsA = s -> s.startsWith("A");
Predicate<String> isLongAndA = isLong.and(startsA); // compose
Predicate<String> isLongOrA = isLong.or(startsA);
Predicate<String> isShort = isLong.negate();
System.out.println(isLong.test("Hello")); // false
System.out.println(isLong.test("Effective")); // true
System.out.println(isLongAndA.test("Alice")); // false (not long enough)
System.out.println(isLongAndA.test("Alexandra")); // true
// ── Function<T,R>: T → R ─────────────────────────────────
Function<String, Integer> length = String::length; // method reference
Function<String, String> toUpper = String::toUpperCase;
Function<String, Integer> upperLen = toUpper.andThen(length); // compose
Function<Integer, Integer> square = n -> n * n;
System.out.println(length.apply("Hello")); // 5
System.out.println(upperLen.apply("hello")); // 5
System.out.println(square.apply(7)); // 49
// ── Consumer<T>: T → void ────────────────────────────────
Consumer<String> print = System.out::println;
Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());
Consumer<String> both = print.andThen(printUpper); // chain
both.accept("java"); // prints "java" then "JAVA"
// ── Supplier<T>: () → T ──────────────────────────────────
Supplier<String> greeting = () -> "Hello, Library!";
Supplier<Double> random = Math::random;
System.out.println(greeting.get());
// ── BiFunction, BiPredicate, BiConsumer ──────────────────
BiFunction<String, Integer, String> repeat = (s, n) -> s.repeat(n);
System.out.println(repeat.apply("ha", 3)); // "hahaha"
// ── Comparator as lambda ──────────────────────────────────
Comparator<String> byLength = (a, b) -> a.length() - b.length();
Comparator<String> byLengthThenAlpha = Comparator
.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder());
}
}Quick Quiz
Tip
Tip
Practice Lambdas Functional Interfaces in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Streams are lazy until terminal operation. Use filter + map + collect for most cases. Parallel streams for heavy CPU work.
Practice Task
Note
Practice Task — (1) Write a working example of Lambdas Functional Interfaces from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Common Mistake
Warning
A common mistake with Lambdas Functional Interfaces is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready java code.