Java Lambda Expressions

Lambda expressions, introduced in Java 8, provide a concise and clean way to represent a functional interface (an interface with exactly one abstract method) as an expression. They enable a functional programming style in Java, reducing boilerplate code significantly.

Why Lambda Expressions?

Before Java 8, passing behavior (like defining what to do when a button is clicked) required creating anonymous inner classes, which were verbose. Lambda expressions replace this with a much shorter syntax.

Without Lambda (Before Java 8)

// Using an anonymous inner class
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Task is running.");
    }
};
task.run();

With Lambda (Java 8+)

Runnable task = () -> System.out.println("Task is running.");
task.run();

The lambda reduces five lines to one.

Lambda Syntax

(parameters) -> expression
(parameters) -> { statements; }
  • Parameters: The inputs to the method (can be empty () or have types inferred).
  • Arrow (->): Separates parameters from the body.
  • Body: A single expression or a block of statements.

Functional Interface

Lambda expressions work with functional interfaces — interfaces that have exactly one abstract method. The @FunctionalInterface annotation confirms this.

@FunctionalInterface
interface Greet {
    void sayHello(String name);
}

public class Main {
    public static void main(String[] args) {
        Greet g = (name) -> System.out.println("Hello, " + name + "!");
        g.sayHello("Alice");   // Hello, Alice!
    }
}

Lambda with Return Value

@FunctionalInterface
interface Multiplier {
    int multiply(int a, int b);
}

public class Main {
    public static void main(String[] args) {
        Multiplier m = (a, b) -> a * b;
        System.out.println("Result: " + m.multiply(6, 7));   // Result: 42
    }
}

Lambda with a Block Body

When the body has multiple statements, use curly braces and an explicit return.

@FunctionalInterface
interface Grade {
    String evaluate(int marks);
}

public class Main {
    public static void main(String[] args) {
        Grade g = (marks) -> {
            if (marks >= 90) return "A";
            else if (marks >= 75) return "B";
            else if (marks >= 60) return "C";
            else return "F";
        };

        System.out.println(g.evaluate(88));   // B
        System.out.println(g.evaluate(55));   // F
    }
}

Built-in Functional Interfaces (java.util.function)

Java 8 introduced the java.util.function package with commonly used functional interfaces.

Predicate<T> – Returns boolean

import java.util.function.Predicate;

Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4));    // true
System.out.println(isEven.test(7));    // false

Function<T, R> – Takes input T, returns output R

import java.util.function.Function;

Function<String, Integer> strLength = s -> s.length();
System.out.println(strLength.apply("Hello"));   // 5

Consumer<T> – Takes input T, returns nothing

import java.util.function.Consumer;

Consumer<String> printUpperCase = s -> System.out.println(s.toUpperCase());
printUpperCase.accept("java");   // JAVA

Supplier<T> – Takes nothing, returns T

import java.util.function.Supplier;

Supplier<String> greeting = () -> "Welcome to Java 8!";
System.out.println(greeting.get());   // Welcome to Java 8!

BiFunction<T, U, R> – Takes two inputs, returns output

import java.util.function.BiFunction;

BiFunction<Integer, Integer, Integer> power = (base, exp) -> (int) Math.pow(base, exp);
System.out.println(power.apply(2, 10));   // 1024

Lambda with Collections – Sorting

Lambda expressions simplify sorting and processing collections.

import java.util.ArrayList;
import java.util.Collections;

ArrayList<String> names = new ArrayList<>();
names.add("Charlie");
names.add("Alice");
names.add("Bob");

// Sort alphabetically using lambda
Collections.sort(names, (a, b) -> a.compareTo(b));
System.out.println(names);   // [Alice, Bob, Charlie]

// Sort by length
names.sort((a, b) -> a.length() - b.length());
System.out.println(names);   // [Bob, Alice, Charlie]

Lambda with forEach

import java.util.Arrays;
import java.util.List;

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Using lambda with forEach
numbers.forEach(n -> System.out.print(n * n + " "));
// Output: 1 4 9 16 25

Method Reference (Shorthand for Lambda)

When a lambda simply calls an existing method, it can be replaced with a method reference using the :: operator.

// Lambda
names.forEach(name -> System.out.println(name));

// Method reference – equivalent and cleaner
names.forEach(System.out::println);
TypeSyntaxExample
Static methodClassName::methodNameMath::sqrt
Instance method (object)object::methodNameSystem.out::println
Instance method (class)ClassName::methodNameString::toUpperCase
ConstructorClassName::newArrayList::new

Summary

  • Lambda expressions provide a short way to implement functional interfaces.
  • Syntax: (parameters) -> body
  • They work only with functional interfaces — interfaces with exactly one abstract method.
  • Built-in functional interfaces in java.util.function: Predicate, Function, Consumer, Supplier.
  • Lambda expressions simplify sorting, event handling, and collection processing.
  • Method references (::) are a clean shorthand when a lambda just calls an existing method.

Leave a Comment

Your email address will not be published. Required fields are marked *