18/04/2023
9 min read
Java

Content:

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:






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.