Introduction to Functional Programming in Java

Introduction
I recently had to learn Java for work and with my affinity for functional programming i rapidly learn how to implement some of the most well known patterns of FP in Java.
Even if Java is most commonly associated with object-oriented-programming we will see in this article how to leverage the power of functional programming with the new features introduce in Java 8.
this article assumed that you already know the most common patterns of FP and don't go deep in details about explaining them but rather focus on their implementation in Java.
If you want to learn more about those FP patterns i wrote an article about them that you'll find here.
Lambda and Functional Interface
In Functional Programming functions are first class citizens , that means that functions are treated as variable and can be pass as parameters to another function or method.
Unfortunately Java does not support first class functions because everything is an object in the Java world,
so how can Java support functional programming with this inconvenient ?
The Anwser is with an interface with a single method, a functional interface. Before Java 8, if you wanted to create a functional interface,
you would have to define a regular interface.Then, you would have to provide an implementation for the single abstract method in the interface.
1
2
3
interface Calculator {
int add(int a, int b);
}
And typically you would have to create a new class that implements that functional interface or an anymous class like in this exemple.
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
Calculator myCalculator = new Calculator() {
@Override
public void doSomething(int a, int b) {
return a + b;
}
};
myCalculator.add(5,6);
}
}
But Java 8 came with Functional Interface that checks at compile time that your interface as only one abstract method.
1
2
3
4
@FunctionalInterface
interface Calculator {
int add(int a, int b);
}
And with lamba which is syntactic sugar that reduce the boiler plate code that you can see above with the anonymous class by a lot.
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
Calculator myCalculator = (a, b) → a + b; // the type of the parameters are inferred
myCalculator.add(5,6);
}
}
Java built-in Functional Interfaces
Java already provide built-in functional interfaces through his package java.util.function. You will use those interfaces 99% of the time instead of creating one by hand, you can find the most important ones in the table below .

Now that we learn about the java built-in functional interfaces we can throw away the Calculator interface that we created earlier and replace it with
the BinaryOperator Functional Interface.
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
BinaryOperator<Interger> myCalculator = (a, b) → a + b;
myCalculator.apply(5,6);
}
}
Method Reference
In Java, method reference is a shorthand way of referring to a method or constructor of a class, without actually invoking the method or
creating an instance of the class.
There are four types of method references in Java:
Reference to a static method: You can refer to a static method of a class using the class name followed by the method name. For example, Integer::parseInt refers to the static parseInt method of the Integer class.
Reference to an instance method of an object of a particular type: You can refer to an instance method of an object of a particular type using the object name followed by the :: operator and the method name.
Reference to an instance method of an arbitrary object of a particular type: You can refer to an instance method of an arbitrary object of a particular type using the class name followed by the :: operator and the method name. For example, String::toUpperCase refers to the instance method toUpperCase of any String object.
Reference to a constructor: You can refer to a constructor using the class name followed by ::new. For example, ArrayList::new refers to the constructor of the ArrayList class.
Method reference can simplify code and make it easier to read and understand, especially when working with functional programming constructs like streams and lambdas.
Let's see an implementation of the first type of method reference by printing the content of an ArrayList.
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("alpha","bravo","charlie","delta"));
list.forEach(System.out::println);
}
}
Functional Composition
In java you can chain mutiple lambas from left to right with the andThen method (like the pipe operator in most FP languages),
and from right to left with the compose method. Let's see an exemple.
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
UnaryOperator<String> capitalize = String::toUpperCase;
Function<String,String[]> splitBySpace = s -> s.split(" ");
Function<String,String> capitalizeAndSeparateByComma = capitalize.andThen(splitBySpace).andThen(s -> String.join(", ", s));
capitalizeAndSeparateByComma.apply("functional progamming");// "FUNCTIONAL, PROGRAMMING
}
}
Currying
To achieve currying in Java it is quite easy you simply have to write a method or a lambda that retrun a lambda.
Let's see an exemple of a curried add lambda that is used to create more specific add1 and add2 lambda.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
UnaryOperator<Integer> add1 = add(1);
UnaryOperator<Integer> add2 = add(2);
add1.apply(6); // 7
add2.apply(6); // 8
}
private static UnaryOperator<Integer> add(int a){
return (b) -> a + b;
}
}
Recursion
Recursion in Java is like recursion in functional programming except that Java does not benefit from tail call optimization.
Let's see an implementation of the famous Fibonacci Series.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
private static final Map<Integer,Integer> memo = new HashMap<>(); // using a HashMap to memoize the fibonacci method
public static void main(String[] args) {
fibonacci(0); // 0
fibonacci(1); // 1
fibonacci(2); // 1
fibonacci(9); // 34
fibonacci(29); // 514229
}
public static int fibonacci(int num) {
if(num < 2 ) return num;
if(memo.containsKey(num)) return memo.get(num);
memo.put(num,fibonacci(num - 1) + fibonacci(num -2));
return memo.get(num);
}
}
Monads
a Monad is an abstract concept from the Category Theory and many functional programming languages derive their concept from it. It is way more complicated than that but but in summary a monad allow us to wrap a value , applied a set of transformations to it, and retrieve the value back with all the transformations applied to it. There are multiple Monads in Java Such as Optional and Stream.
for our exemple we will use the stream api to sort a list of bingo number , maked them upper case and keep only the bingo numbers that starts with G.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
List<String> someBingoNumbers = Arrays.asList(
"N40", "N36", "B12", "B6", "G53", "G49", "G60", "G50", "g64",
"I26", "I17", "I29", "071"
);
someBingoNumbers.stream()
.sorted()
.map(String::toUpperCase)
.filter(number -> number.startsWith("G"); // [G49,G50,G53,G60,G64]
}
}
Conclusion
In this article we saw how to apply the most well known FP patterns in Java.
You can see the syntax can seem a bit weird especially if you only did OOP or other kind of imperative programming so far.
Java is not perfectly suited for full functional programming either that is why i think mixing OOP and FP is the way to go in Java.