Functional Programming in Java: From Lambdas to Stream API

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Functional Programming in Java



For a long time, Java was exclusively associated with object-oriented programming (OOP). However, with the release of Java 8 in 2014, the language made a powerful leap towards the functional paradigm. Lambda expressions, functional interfaces, and the Stream API fundamentally changed the approach to writing code, making it more concise, readable, and less error-prone.



Functional programming in Java is not a replacement for OOP, but a powerful addition. It allows you to write declarative code, where you describe what needs to be done, rather than how. In this article, we will break down the key concepts that will help you master the functional style in Java and show practical examples.



1. Lambda Expressions and Functional Interfaces



The foundation of functional programming in Java is lambda expressions — a compact way to implement anonymous functions. Syntactically, a lambda consists of a parameter list, an arrow (->), and a body:



(parameters) -> { body }


Lambdas always work in conjunction with functional interfaces. A functional interface is an interface that contains exactly one abstract method (e.g., Runnable, Comparator, Predicate).



Practical Example: Replacing an Anonymous Class



Let's compare the old (OOP) and new (functional) approaches:



// Old way (anonymous class)Runnable taskOld = new Runnable() {    @Override    public void run() {        System.out.println("Hello from anonymous class!");    }};

// New way (lambda)Runnable taskNew = () -> System.out.println("Hello from lambda!");

// ExecutiontaskNew.run();


As you can see, the lambda reduced the code from 6 lines to 1, while retaining full functionality.



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



Java provides a rich set of built-in interfaces. Here are the four main ones:



  • Predicate<T> — takes an object, returns boolean. Used for filtering.
  • Function<T, R> — takes an object of one type, returns an object of another type. Used for transformations.
  • Consumer<T> — takes an object, returns nothing (void). Used for side effects (printing output).
  • Supplier<T> — takes nothing, returns an object. Used for lazy data generation.


Example with Predicate:



import java.util.function.Predicate;

public class Main { public static void main(String[] args) { Predicate<Integer> isEven = n -> n % 2 == 0; System.out.println(isEven.test(10)); // true System.out.println(isEven.test(7)); // false }}


2. Stream API: Working with Collections in a Functional Style



The Stream API is the gem of functional programming in Java. A stream (Stream) is a sequence of elements supporting sequential and parallel operations. The main advantage: you do not modify the source data, but create a new result.



The Stream API is built on three stages:

  1. Creating a stream (from a collection, array, or generator).
  2. Intermediate operations (filtering, sorting, transformation). Return a new Stream.
  3. Terminal operation (collecting the result, counting, searching). Ends the stream's work.


Example: Filtering and Transforming a List



Suppose we have a list of strings, and we want to get only those longer than 3 characters, convert them to uppercase, and sort them.



import java.u                

Blogs

Book Recommendations