Java programming language

Functional programming

Introduction to functional programming

  

Reference version: These notes use Java 25 LTS. Most lambda and stream concepts were introduced in Java 8, while some examples also mention later additions such as Stream.toList().

Functional programming is a programming paradigm (that is, a way of designing and implementing programs) that focuses on evaluating functions and composing transformations. Its theoretical foundations come from lambda calculus, a formal system developed in the 1930s to describe function definition, application and recursion.

The functional paradigm is mainly declarative. We describe the result and the transformations to apply instead of detailing every step used to solve the problem. In contrast, imperative programming uses commands, assignments and explicit control structures to describe how the program must perform the task.

1. Some functional programming features

Functional programming relies on some fundamental concepts:

Imperative languages can produce side effects in external elements. They also have functions, just like functional programming has, but in this case functions are not mathematical definitions, but a group of sentences that can be called from different parts of the program. Taking into account possible side effects, we might have different results when calling a function many times with the same arguments. Let’s have a look at the following code written in C:

int externalValue = 1;
int aFunction(int param)
{
    externalValue++;
    return externalValue + param;
}

If we call the function as aFunction(1), it will return 3. But if we call it again with the same arguments (aFunction(1)), it will return 4, and so on. This function is not pure because it changes external state. Functional programming does not make such code impossible, but it encourages us to isolate side effects and use pure functions whenever appropriate.

2. Functional languages

If we are looking for a functional language, we must distinguish between:

3. An introductory example

Just to have a first experience with the functional paradigm, let’s see how to solve a typical problem. We are going to use a popular language, such as Java, to illustrate it. We have a list of Person objects, each one with its own name and age. If we want to get the people that are adults (i.e. their age is greater or equal than 18), we would do it this way with traditional, imperative programming:

List<Person> adultPeople = new ArrayList<>();
for (int i = 0; i < people.size(); i++)
{
    if (people.get(i).getAge() >= 18)
        adultPeople.add(people.get(i));
}

We traverse the original list and add every person aged 18 or older to a new list. If we use the functional features introduced in Java 8, we can solve the same problem like this:

List<Person> adultPeople = people.stream()
                                 .filter(p -> p.getAge() >= 18)
                                 .collect(Collectors.toList()); 

The code describes the required transformation directly and avoids manual index management. Function composition can also be seen here: stream() creates the stream, filter() defines which elements remain, and collect() builds the result.

Since Java 16, we can use toList() when an unmodifiable result is suitable:

List<Person> adultPeople = people.stream()
                                 .filter(p -> p.getAge() >= 18)
                                 .toList();

If the resulting list must be mutable, we can request a specific implementation:

List<Person> adultPeople = people.stream()
                                 .filter(p -> p.getAge() >= 18)
                                 .collect(Collectors.toCollection(ArrayList::new));

4. Functional programming in Java

Java is a multiparadigm language. Since Java 8, lambda expressions, functional interfaces, method references and streams have made it possible to use a functional style in many parts of an application. These features do not make Java a pure functional language, and they do not make objects immutable automatically.