For working professionals
For fresh graduates
More
6. JDK in Java
7. C++ Vs Java
16. Java If-else
18. Loops in Java
20. For Loop in Java
46. Packages in Java
53. Java Collection
56. Generics In Java
57. Java Interfaces
60. Streams in Java
63. Thread in Java
67. Deadlock in Java
74. Applet in Java
75. Java Swing
76. Java Frameworks
78. JUnit Testing
81. Jar file in Java
82. Java Clean Code
86. Java 8 features
87. String in Java
93. HashMap in Java
98. Enum in Java
101. Hashcode in Java
105. Linked List in Java
109. Array Length in Java
111. Split in java
112. Map In Java
115. HashSet in Java
118. DateFormat in Java
121. Java List Size
122. Java APIs
128. Identifiers in Java
130. Set in Java
132. Try Catch in Java
133. Bubble Sort in Java
135. Queue in Java
142. Jagged Array in Java
144. Java String Format
145. Replace in Java
146. charAt() in Java
147. CompareTo in Java
151. parseInt in Java
153. Abstraction in Java
154. String Input in Java
156. instanceof in Java
157. Math Floor in Java
158. Selection Sort Java
159. int to char in Java
164. Deque in Java
172. Trim in Java
173. RxJava
174. Recursion in Java
175. HashSet Java
177. Square Root in Java
190. Javafx
Functional programming is a programming style that treats computation as the evaluation of mathematical functions. In Java, especially after version 8, this style became more accessible through features like lambda expressions, method references, and the Stream API. Unlike the traditional object-oriented approach, functional programming focuses on writing clean, reusable, and side-effect-free functions.
It is widely used for processing collections, handling asynchronous programming, and writing more readable and maintainable code. In this blog, you’ll learn what functional programming means in Java, how to implement it, and why it matters for modern software development.
To master concepts like these, Software Engineering courses can provide structured learning and hands-on experience.
Functional programming in Java is a way of writing code where functions are first-class citizens. This means functions can be passed around as arguments, returned from other functions, and assigned to variables. Java supports this through lambda expressions and functional interfaces. It encourages immutability, pure functions, and declarative coding.
Here’s a basic example using functional programming:
import java.util.Arrays;
import java.util.List;
public class FunctionalExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Anisha", "Diksha", "Charu");
names.forEach(name -> System.out.println(name));
}
}
Output:
Anisha
Diksha
Char
Explanation:
This code uses a lambda expression to iterate over a list and print each name. It’s concise and avoids boilerplate code.
Must read: Array in Java: Types, Operations, Pros & Cons
Unlike purely functional languages like Haskell, functional programming in Java allows side effects and mutable data. Purely functional programming strictly follows principles like immutability, no side-effects, and referential transparency. Java blends functional and object-oriented paradigms, allowing flexibility while encouraging cleaner coding practices.
Example: In Java, you can mutate data, but functional style avoids it by choice.
int x = 10;
x = x + 5; // mutation
In a pure language, x would never change.
Take your skills to the next level with these highly rated courses.
Functional programming relies on several key principles. These include first-class functions, pure functions, immutability, referential transparency, and recursion. Each concept helps make your code more predictable and easier to test.
Let’s look at a pure function:
public class MathUtils {
public static int square(int x) {
return x * x;
}
}
Output (for input 4):
16
Explanation:
The function always returns the same result for the same input and has no side effects.
A pure function is one that always gives the same output for the same input and does not modify any external state. This makes code predictable and easy to test.
public class Greetings {
public static String greet(String name) {
return "Hello, " + name;
}
}
Output:
Hello, Janki
Explanation:
The greet method returns a message based only on its input, without changing any external variable.
Higher-order functions take other functions as arguments or return functions as results. Java supports this using functional interfaces.
import java.util.function.Function;
public class HigherOrder {
public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
System.out.println(applyFunction(square, 5));
}
public static int applyFunction(Function<Integer, Integer> func, int value) {
return func.apply(value);
}
}
Output:
25
Explanation:
The applyFunction method takes a function and a value, applying the function to the value.
Lambda expressions are short blocks of code that take in parameters and return a value. They’re used to implement functional interfaces in a clean and concise way.
Syntax:
(parameter) -> expression
Example:
List<String> names = Arrays.asList("Tom", "Jerry");
names.forEach(n -> System.out.println(n));
Output:
Tom
Jerry
Explanation:
The lambda expression n -> System.out.println(n) is passed to the forEach method.
A functional interface has just one abstract method. Java provides several in the java.util.function package.
Example:
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Java"));
Output:
4
Explanation:
The lambda returns the length of the input string.
Method references are a shorthand for calling existing methods using :: syntax. They improve readability.
import java.util.Arrays;
import java.util.List;
public class MethodRef {
public static void main(String[] args) {
List<String> names = Arrays.asList("A", "B", "C");
names.forEach(System.out::println);
}
}
Output:
A
B
C
Explanation:
System.out::println is a method reference that replaces the lambda expression for printing.
Java streams let you perform functional-style operations on collections. You can use methods like map(), filter(), and reduce().
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
numbers.stream().map(n -> n * 2).forEach(System.out::println);
Output:
2
4
6
8
Explanation:
Each number in the list is doubled using map() and printed with forEach().
To write functional-style code in Java, use pure functions, avoid shared state, use immutability, and leverage lambda expressions and streams. Prefer declarative constructs like filter() and map() over loops.
Example:
List<String> words = Arrays.asList("java", "code", "blog");
words.stream().filter(w -> w.startsWith("c")).forEach(System.out::println);
Output:
code
Explanation:
This filters words starting with "c" and prints them.
Let’s refactor an imperative Java 7 example into a functional Java 8 style:
Java 7 Style:
for (String name : names) {
if (name.startsWith("A")) {
System.out.println(name);
}
}
Java 8 Style:
names.stream().filter(n -> n.startsWith("A")).forEach(System.out::println);
Explanation: The Java 8 version is more concise and easier to read using stream and lambda.
Imperative programming describes how to do things step by step. Declarative programming focuses on what you want to achieve. Functional programming encourages the declarative style.
Example:
// Imperative
int sum = 0;
for (int n : numbers) sum += n;
// Declarative
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
Explanation:
The declarative version uses stream operations to calculate the sum.
Functional programming in Java helps developers write cleaner, more concise, and maintainable code. It focuses on using functions and avoids changing state or mutable data.
Common use cases include:
Avoid overly complex lambda expressions. Don’t use streams when a simple loop is clearer. Understand the lazy nature of streams to prevent bugs. Overuse of functional features can reduce code readability, especially for teams new to the style.
Functional programming in Java allows for cleaner, more maintainable, and expressive code. With lambda expressions, method references, and streams, Java now supports functional constructs while keeping its object-oriented core. Learning this paradigm can significantly improve your problem-solving skills in modern Java development.
Yes, Java allows both paradigms to coexist. You can use functional constructs like lambdas and streams inside object-oriented code. This hybrid approach offers flexibility—leveraging functional style for clean, side-effect-free logic while retaining OOP features like encapsulation and inheritance.
Immutability ensures that data cannot be modified after it's created. In functional programming, this reduces side effects, simplifies debugging, and improves thread safety. Java supports immutability using final variables and immutable classes like String, LocalDate, and collections from java.util.Collections.
Yes, functional programming is well-suited for multithreaded environments. Pure functions and immutability prevent shared mutable state, minimizing synchronization issues. Java’s parallel streams and functional constructs help write clean, thread-safe code without complex concurrency control.
While functional programming improves readability and maintainability, it can reduce clarity when overused or used incorrectly. Complex lambdas, deep stream chains, and limited debugging support can make code harder to trace. It also has a steeper learning curve for developers new to the paradigm.
Java streams use lazy evaluation for intermediate operations like map() and filter()—these don’t execute until a terminal operation (like collect() or forEach()) is called. Eager evaluation executes immediately. Lazy evaluation improves performance by avoiding unnecessary computations.
Yes, you can define your own functional interfaces using the @FunctionalInterface annotation. A custom functional interface must contain exactly one abstract method. This allows you to tailor behavior-specific contracts that can be implemented using lambda expressions or method references.
Handling checked exceptions inside lambdas or stream operations can be tricky because most functional interfaces don’t allow them. Common solutions include wrapping exceptions in try-catch blocks inside the lambda or using custom wrapper functions to handle them more cleanly.
map() transforms each element in a stream into another value, while flatMap() flattens a stream of streams into a single stream. Use flatMap() when dealing with nested structures like lists of lists or optional values that return streams.
In general, functional code may have slightly more overhead due to stream pipelines, object creation, or method calls. However, modern JVM optimizations and parallel stream capabilities often balance this. Performance depends on the use case and how well the code is structured.
Yes, lambdas can access effectively final local variables from their enclosing scope. These are variables that are not modified after being assigned. This constraint ensures safe behavior and supports functional programming principles like immutability and statelessness.
Functional constructs like pure functions and immutability make unit testing easier. Since outputs depend only on inputs and there are no side effects, tests become more predictable and isolated. This also reduces reliance on mocks or shared test setup, simplifying maintenance.
Take the Free Quiz on Java
Answer quick questions and assess your Java knowledge
Author|900 articles published
Previous
Next
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918068792934
1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.