Java Functional Programming
Jakob Jenkov |
The term Java functional programming refers to functional programming in Java. Functional programming in Java has not been easy historically, and there were even several aspects of functional programming that were not even really possible in Java. In Java 8 Oracle made an effort to make functional programming easier, and this effort did succeed to some extent. In this Java functional programming tutorial I will go through the basics of functional programming, and what parts of it that are possible in Java.
Functional Programming Basics
Functional programming contains the following key concepts:
- Functions as first class objects
- Pure functions
- Higher order functions
Pure functional programming has a set of rules to follow too:
- No state
- No side effects
- Immutable variables
- Favour recursion over looping
These concepts and rules will be explained throughout the rest of this tutorial
Even if you do not follow all of these rules all the time, you can still benefit from the functional programming ideas in your applications. As you will see, functional programming is not the right tool for every problem out there. Especially the idea of "no side effects" makes it hard to e.g. write to a database (that is a side effect). You need to learn what problems functional programming is good at solving, and which it is not.
Functions as First Class Objects
In the functional programming paradigm, functions are first class objects in the language. That means that you can create an "instance" of a function, as have a variable reference that function instance, just like a reference to a String, Map or any other object. Functions can also be passed as parameters to other functions.
In Java, methods are not first class objects. The closest we get is Java Lambda Expressions. I will not cover Java lambda expressions here, as I have covered them in both text and video in my Java Lambda expression tutorial.
Pure Functions
A function is a pure function if:
- The execution of the function has no side effects.
- The return value of the function depends only on the input parameters passed to the function.
Here is an example of a pure function (method) in Java:
public class ObjectWithPureFunction{ public int sum(int a, int b) { return a + b; } }
Notice how the return value of the sum()
function only depends on the input parameters.
Notice also that the sum()
has no side effects, meaning it does not modify any state
(variables) outside the function anywhere.
Contrarily, here is an example of a non-pure function:
public class ObjectWithNonPureFunction{ private int value = 0; public int add(int nextValue) { this.value += nextValue; return this.value; } }
Notice how the method add()
uses a member variable to calculate its return value,
and it also modifies the state of the value
member variable, so it has a side effect.
Higher Order Functions
A function is a higher order function if at least one of the following conditions are met:
- The function takes one or more functions as parameters.
- The function returns another function as result.
In Java, the closest we can get to a higher order function is a function (method) that takes one or more lambda expressions as parameters, and returns another lambda expression. Here is an example of a higher order function in Java:
public class HigherOrderFunctionClass { public <T> IFactory<T> createFactory(IProducer<T> producer, IConfigurator<T> configurator) { return () -> { T instance = producer.produce(); configurator.configure(instance); return instance; } } }
Notice how the createFactory()
method returns a lambda expression as result. This is the first
condition of a higher order function.
Notice also that the createFactory()
method takes two instances as parameters which are both implementations
of interfaces (IProducer
and IConfigurator
). Java lambda expressions have to implement
a functional interface, remember?
Imagine the interfaces looks like this:
public interface IFactory<T> { T create(); }
public interface IProducer<T> { T produce(); }
public interface IConfigurator<T> { void configure(T t); }
As you can see, all of these interfaces are functional interfaces. Therefore they can be implemented by
Java lambda expressions - and therefore the createFactory()
method is a higher order function.
Higher order functions are also covered with different examples in the text about Higher Order Functions
No State
As mentioned in the beginning of this tutorial, a rule of the functional programming paradigm is to have no state. By "no state" is typically meant no state external to the function. A function may have local variables containing temporary state internally, but the function cannot reference any member variables of the class or object the function belongs to.
Here is an example of a function that uses no external state:
public class Calculator { public int sum(int a, int b) { return a + b; } }
Contrarily, here is an example of a function that uses external state:
public class Calculator { private int initVal = 5; public int sum(int a) { return initVal + a; } }
This function clearly violates the no state rule.
No Side Effects
Another rule in the functional programming paradigm is that of no side effects. This means, that a function cannot change any state outside of the function. Changing state outside of a function is referred to as a side effect.
State outside of a function refers both to member variables in the class or object the function, and member variables inside parameters to the functions, or state in external systems like file systems or databases.
Immutable Variables
A third rule in the functional programming paradigm is that of immutable variables. Immutable variables makes it easier to avoid side effects.
Favour Recursion Over Looping
A fourth rule in the functional programming paradigm is to favour recursion over looping. Recursion uses function calls to achieve looping, so the code becomes more functional.
Another alternative to loops is the Java Streams API. This API is functionally inspired.
Functional Interfaces
A functional interface in Java is an interface that only has one abstract method. By an abstract method is meant only one method which is not implemented. An interface can have multiple methods, e.g. default methods and static methods, both with implementations, but as long as the interface only has one method that is not implemented, the interface is considered a functional interface.
Here is an example of a functional interface:
public interface MyInterface { public void run(); }
Here is another example of a functional interface with a default method and a static method too:
public interface MyInterface2 { public void run(); public default void doIt() { System.out.println("doing it"); } public static void doItStatically() { System.out.println("doing it statically"); } }
Notice the two methods with implementations. This is still a functional interface, because only run()
is not implemented (abstract). However, if there were more methods without implementation, the interface would
no longer be a functional interface, and could thus not be implemented by a Java lambda expression.
Tweet | |
Jakob Jenkov |