Table of contents
1.
Introduction
2.
What is Functional Programming?
2.1.
Key Concepts of Functional Programming
3.
Principles and Concepts of Functional Programming in Java
3.1.
Pure Functions
3.2.
Immutability
3.3.
Higher-Order Functions
4.
Functional Programming vs Purely Functional Programming
4.1.
Example: Functional Programming in Java
5.
How to Implement Functional Programming in Java?
5.1.
1. Lambda Expressions
5.2.
2. Functional Interfaces
5.3.
3. Streams API
6.
Characteristics of Functional Programming Language
7.
Functional Programming in Java Techniques
7.1.
Lambda Expressions
7.2.
Stream API
7.3.
Method References
8.
Refactoring Some Functions from Java 7 to Java 8
8.1.
Java 7 (Imperative Style)
8.2.
Java 8 (Functional Style)
9.
Imperative Vs Declarative Programming
9.1.
Imperative Programming
9.2.
Declarative Programming
10.
Why does Functional Programming in Java Matter?
10.1.
Easier to Understand and Maintain
10.2.
Better Concurrency Support
10.3.
Reusability and Composition
11.
Frequently Asked Questions
11.1.
What is functional programming in Java?
11.2.
What are lambda expressions in Java?
11.3.
What is the difference between imperative and declarative programming?
12.
Conclusion
Last Updated: Feb 3, 2025
Medium

Functional Programming in Java

Author Pallavi singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Functional programming in Java is a programming paradigm that treats computation as the evaluation of mathematical functions while avoiding changing states and mutable data. Introduced in Java 8, functional programming allows developers to write cleaner, more concise, and more readable code using lambda expressions, functional interfaces, and stream API.

Functional Programming in Java

In this article, we will discuss functional programming, its characteristics, how to implement it in Java, and the difference between imperative and declarative programming.

What is Functional Programming?

Functional programming is a programming style where computations are done using functions without modifying the state or data. It is based on the concept of immutability and pure functions.

Key Concepts of Functional Programming

  1. Pure Functions - Functions that always return the same output for the same input and do not modify external variables.
     
  2. Immutability - Data cannot be changed after being created.
     
  3. First-Class Functions - Functions are treated as variables and can be passed as arguments.
     
  4. Higher-Order Functions - Functions that take other functions as arguments or return functions.
     
  5. Recursion - Using functions to achieve looping instead of using iterative loops.

Principles and Concepts of Functional Programming in Java

Functional programming is built on a few key principles that make it different from other programming styles. The main idea is to treat computation as the evaluation of mathematical functions. Let's discuss the core concepts in more detail: 

Pure Functions

A pure function is a function that, given the same input, will always return the same output and does not have any side effects. This means it doesn't change anything outside of itself. For example, if you have a function that adds two numbers, it will always return the same result for the same inputs.

For example: 

public class PureFunctionExample {
    public static void main(String[] args) {
        int result = add(3, 4);
        System.out.println("The result is: " + result);
    }


    public static int add(int a, int b) {
        return a + b;
    }
}


In this code, the add function is pure because it only uses its input parameters and doesn't change anything outside of itself.

Immutability

Immutability means that once you create an object, you can't change it. This is important in functional programming because it helps avoid side effects. When you use immutable objects, you can be sure that they won't change unexpectedly.

For example: 

public final class ImmutablePerson {
    private final String name;
    private final int age;

    public ImmutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}


In this example, the ImmutablePerson class has final fields, which means they can't be changed after the object is created.

Higher-Order Functions

Higher-order functions are functions that take other functions as arguments or return functions as results. This is a powerful concept because it allows you to write more flexible and reusable code.

For example: 

public class HigherOrderFunctionExample {
    public static void main(String[] args) {
        int result = operate(5, 3, (a, b) -> a + b);
        System.out.println("The result is: " + result);
    }
    public static int operate(int a, int b, java.util.function.BiFunction<Integer, Integer, Integer> function) {
        return function.apply(a, b);
    }
}


In this code, the operate function takes two integers and a function as arguments. The function is then applied to the integers.

Functional Programming vs Purely Functional Programming

Functional Programming in Java follows a hybrid approach as Java is primarily an object-oriented language. It allows both imperative and functional programming styles.

However, Purely Functional Programming strictly adheres to functional concepts where functions have no side effects, and variables are immutable. Languages like Haskell follow purely functional programming.

Example: Functional Programming in Java

import java.util.Arrays;
import java.util.List;
public class FunctionalExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
               .map(n -> n * n) // Function applied to each element
               .forEach(System.out::println); // Print each squared number
    }
}
You can also try this code with Online Javascript Compiler
Run Code


Output:

1
4
9
16
25


Here, map() is a functional approach where we apply a function to each element in a list without modifying the original list.

How to Implement Functional Programming in Java?

Java provides various features to implement functional programming, primarily introduced in Java 8.

1. Lambda Expressions

Lambda expressions provide a concise way to define functions.

interface MyFunction {
    int calculate(int a, int b);
}
public class LambdaExample {
    public static void main(String[] args) {
        MyFunction addition = (a, b) -> a + b;
        System.out.println(addition.calculate(5, 10));
    }
}
You can also try this code with Online Javascript Compiler
Run Code


Output:

15

2. Functional Interfaces

A functional interface contains only one abstract method.

import java.util.function.Predicate;
public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        Predicate<Integer> isEven = num -> num % 2 == 0;
        System.out.println(isEven.test(4)); // true
    }
}
You can also try this code with Online Javascript Compiler
Run Code

3. Streams API

Streams allow processing collections in a functional way.

import java.util.Arrays;
import java.util.List;
public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Jack");
        names.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}
You can also try this code with Online Javascript Compiler
Run Code


Output:

JOHN
JANE
JACK

Characteristics of Functional Programming Language

  1. First-Class Functions - Functions can be assigned to variables, passed as arguments, and returned from other functions.
     
  2. Pure Functions - Functions do not cause side effects.
     
  3. Immutability - Data structures do not change after they are created.
     
  4. Referential Transparency - A function call can be replaced with its result without changing the program behavior.
     
  5. Declarative Code - Code is written in an expressive way instead of focusing on how to achieve results.

Functional Programming in Java Techniques

Functional programming techniques help you write code that is easier to understand and maintain. Let's look at some common techniques used in functional programming.

Lambda Expressions

Lambda expressions are a way to represent functions in a concise way. They are especially useful in Java for implementing functional interfaces.

For example: 

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class LambdaExpressionExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");


        // Using a lambda expression to filter names
        Predicate<String> startsWithA = name -> name.startsWith("A");
        names.stream()
             .filter(startsWithA)
             .forEach(System.out::println);
    }
}


In this example, the lambda expression name -> name.startsWith("A") is used to filter names that start with the letter "A".

Stream API

The Stream API in Java is a powerful tool for processing collections of objects. It allows you to perform operations like filtering, mapping, and reducing in a functional style.

For example: 

import java.util.Arrays;
import java.util.List;
public class StreamApiExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);


        // Using Stream API to filter, map, and reduce
        int sumOfSquares = numbers.stream()
                                  .filter(n -> n % 2 == 0) // Filter even numbers
                                  .map(n -> n * n)         // Square each number
                                  .reduce(0, Integer::sum); // Sum the squares


        System.out.println("Sum of squares of even numbers: " + sumOfSquares);
    }
}


In this example, the Stream API is used to filter even numbers, square each number, and then sum the squares.

Method References

Method references are a way to refer to methods directly without writing a lambda expression. They can make your code more readable and concise.

For example: 

import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        // Using method reference to print names
        names.forEach(System.out::println);
    }
}


In this example, System.out::println is a method reference that refers to the println method of System.out.

Refactoring Some Functions from Java 7 to Java 8

Java 7 (Imperative Style)

import java.util.ArrayList;
import java.util.List;
public class Java7Example {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        
        for (String name : names) {
            System.out.println(name.toUpperCase());
        }
    }
}
You can also try this code with Online Javascript Compiler
Run Code

Java 8 (Functional Style)

import java.util.Arrays;
import java.util.List;
public class Java8Example {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        names.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}
You can also try this code with Online Javascript Compiler
Run Code

Here, Java 8 simplifies the code using Streams and Lambda expressions.

Imperative Vs Declarative Programming

Imperative Programming

  • Focuses on how to achieve the result.
     
  • Uses loops and conditionals.
     
  • Example:
int sum = 0;
for (int i = 1; i <= 5; i++) {
    sum += i;
}
System.out.println(sum);
You can also try this code with Online Javascript Compiler
Run Code


Output: 

15

Declarative Programming

  • Focuses on what to achieve.
     
  • Uses functions and expressions.
     
  • Example:
import java.util.stream.IntStream;
public class DeclarativeExample {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 5).sum();
        System.out.println(sum);
    }
}
You can also try this code with Online Javascript Compiler
Run Code


Output: 

15


Declarative programming makes the code more readable and concise.

Why does Functional Programming in Java Matter?

Functional programming is more than just a different way to write code. It offers many benefits that can make your programs more reliable, easier to maintain, and more efficient.

Easier to Understand and Maintain

One of the biggest advantages of functional programming is that it makes code easier to understand and maintain. By using pure functions and avoiding side effects, you can be sure that a function will always behave the same way given the same inputs. This makes it much easier to reason about your code and debug any issues.

For example, consider a function that calculates the factorial of a number. In functional programming, this function would be pure and have no side effects:

public class FunctionalProgrammingExample {
    public static void main(String[] args) {
        int number = 5;
        int factorial = calculateFactorial(number);
        System.out.println("Factorial of " + number + " is: " + factorial);
    }


    public static int calculateFactorial(int n) {
        if (n <= 1) {
            return 1;
        } else {
            return n * calculateFactorial(n - 1);
        }
    }
}
You can also try this code with Online Java Compiler
Run Code


In this example, the calculateFactorial function is pure. It takes an integer as input and returns the factorial of that integer. Because it has no side effects, you can easily understand what it does by looking at its definition.

Better Concurrency Support

Functional programming also makes it easier to write concurrent programs. Because pure functions don't have side effects, you don't have to worry about race conditions or other concurrency issues. This makes it much easier to write code that can run in parallel.

For example, consider a program that processes a list of numbers and calculates the sum of their squares. In functional programming, you can use the Stream API to process the list in parallel:

import java.util.Arrays;
import java.util.List;
public class ConcurrencyExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // Using Stream API to process the list in parallel
        int sumOfSquares = numbers.parallelStream()
                                  .mapToInt(n -> n * n)
                                  .sum();

        System.out.println("Sum of squares: " + sumOfSquares);
    }
}
You can also try this code with Online Java Compiler
Run Code


In this example, the parallelStream method is used to process the list in parallel. Because the operations are pure, you don't have to worry about concurrency issues.

Reusability and Composition

Functional programming encourages the use of small, reusable functions. These functions can be composed together to create more complex behavior. This makes your code more modular and easier to reuse.

For example, consider a program that processes a list of strings and converts them to uppercase. You can write a small function to convert a single string to uppercase and then use the Stream API to apply this function to the entire list:

import java.util.Arrays;
import java.util.List;
public class ReusabilityExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // Using a reusable function to convert strings to uppercase
        List<String> upperCaseNames = names.stream()
                                          .map(FunctionalProgrammingExample::toUpper)
                                          .toList();
        System.out.println("Uppercase names: " + upperCaseNames);
    }
    public static String toUpper(String str) {
        return str.toUpperCase();
    }
}
You can also try this code with Online Java Compiler
Run Code


In this example, the toUpper function is a reusable function that converts a single string to uppercase. This function can be used with the Stream API to convert the entire list of strings to uppercase.

Frequently Asked Questions

What is functional programming in Java?

Functional programming is a programming style that focuses on using functions to process data while avoiding changing the state or mutable data.

What are lambda expressions in Java?

Lambda expressions are anonymous functions that allow writing short, functional-style code. They are primarily used in Streams API and functional interfaces.

What is the difference between imperative and declarative programming?

Imperative programming focuses on how to achieve a result using step-by-step instructions, while declarative programming focuses on what the result should be using expressions and functions.

Conclusion

In this article, we learned Functional Programming in Java, its key concepts, and its benefits. We discussed how lambda expressions, functional interfaces, streams, and method references make code cleaner and more efficient.Understanding these features helps developers write modular, scalable, and maintainable Java applications with improved performance and readability. 

Live masterclass