Understanding Functional Programming in Java 8

Paul ‘Tofunmi O.
Zero To Production
Published in
6 min readApr 15, 2019

Learn about lambda, filter, streams, method reference, consumer and predicate.

Photo by Tevin Trinh on Unsplash

What is functional programming?

Functional programming is a programming paradigm — a style of building the structure and elements of computer programs — that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data — Wikipedia

In functional programming, computations are declared in programming functions. It is declarative because computation logic is expressed without describing the control flow — expressions are used to tell the computer what needs to be done without stating how it should be done. The functions in functional programming are different from functions in imperative programming because functions in imperative produce an output based on the passed arguments whereas functions in imperative programming might rely on state values (or properties of a class) to decide what will be returned.

What is Functional programming in Java?

In Java 8 release, the creators of Java introduced functional programming with the flagship lambda expressions. In essence, it necessitated a shift in perspective from imperative, object-oriented thinking to declarative functional thinking.

We will create a Bucket class with properties: name, description and priority (on a scale 1 -10 increasing from least important to most important) to see how this works. Bucket class comes from the idea of a bucket list of places to visit. For example, you might plan to visit the seven wonders before your next birthday. So we create our model to hold this information.

package com.zerotoproduction;

public class Bucket {

private String name;
private String description;
private int priority;

public Bucket(String name, String description, int priority) {
this.name = name;
this.description = description;
this.priority = priority;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public int getPriority() {
return priority;
}

public void setPriority(int priority) {
this.priority = priority;
}

@Override
public String toString() {
return "Bucket{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
", priority=" + priority +
'}';
}


}

Now, let us create a class to run our program

package com;

import com.zerotoproduction.Bucket;

import java.util.ArrayList;
import java.util.List;

public class BucketTestMain {

public static void printBuckets1(List<Bucket> buckets)
{

for (Bucket b: buckets)

if (b.getPriority() > 7)

System.out.println(b);

}

public static void printBuckets2(List<Bucket> buckets)
{

buckets.stream()

.filter(bucket -> bucket.getPriority() > 7)

.forEach(bucket -> System.out.println(bucket));

}

public static void main(String[] args){
List<Bucket> myBuckets = new ArrayList<Bucket>();
myBuckets.add(new Bucket("Visit Coliseum, Rome, Italy", "The Coliseum, also known as the Flavian Amphitheatre, is an oval amphitheatre in the centre of the city of Rome, Italy. Built of travertine, tuff, and brick-faced concrete, it is the largest amphitheatre ever built", 1));
myBuckets.add(new Bucket("Visit Christ the Redeemer Statue, Rio de Janeiro, Brazil", "Christ the Redeemer is an Art Deco statue of Jesus Christ in Rio de Janeiro, Brazil, created by French sculptor Paul Landowski", 3)); myBuckets.add(new Bucket("Visit Taj Mahal, India", "The Taj Mahal is an ivory-white marble mausoleum on the south bank of the Yamuna river in the Indian city of Agra. It was commissioned in 1632", 6)); myBuckets.add(new Bucket("Visit Great Wall of China, China", "Built between the 5th century B.C. and the 16th century, the Great Wall of China is a stone-and-earth fortification created to protect the borders of the Chinese Empire from invading Mongols. The Great Wall is actually a succession of multiple walls spanning approximately 4,000 miles, making it the world's longest manmade structure", 8));


BucketTestMain.printBuckets2(myBuckets);
System.out.printf("%n%n");
System.out.printf("---Imperative---%n%n");
BucketTestMain.printBuckets1(myBuckets);
}

}

In our main method, we have initialized the ArrayList with four buckets. However, we have two print methods. The first uses imperative pattern with a for statement for looping and if statement to ensure that only buckets with priority higher than 7 is returned.

The second method makes use of functional programming paradigms. Let’s dig deeper

buckets.stream()

.filter(bucket -> bucket.getPriority() > 7)

.forEach(bucket -> System.out.println(bucket));

buckets.stream returns a Stream Interface holding buckets like so: Stream<Bucket>. Imagine a stream to be consisting of elements. So, we can replace buckets.stream() like so:

Stream<Bucket> bucketStream = buckets.stream();
bucketStream
.filter(bucket -> bucket.getPriority() > 7)
.forEach(bucket -> System.out.println(bucket));

The stream can be filtered to return a stream consisting of elements that match a predicate.

Looking into the source code, you can see that the Stream Interface has a filter method that returns a Stream and accepts an argument of type Predicate.

public interface Stream<T> extends BaseStream<T, Stream<T>> {

Stream<T> filter(Predicate<? super T> predicate);
}

The Predicate handles the task of determining what passes through our filter. In the docs, Predicate is a functional interface with a test method.

@FunctionalInterface
public interface Predicate<T> {

/**
* Evaluates this predicate on the given argument.
*
*
@param t the input argument
*
@return {@code true} if the input argument matches the predicate,
* otherwise {
@code false}
*/
boolean test(T t);

Armed with this knowledge, we can replace our filtering function like so:

buckets.stream()
.filter(new Predicate<Bucket>() {
public boolean test(Bucket bucket){
return bucket.getPriority() > 7;
}
})
.forEach(bucket -> System.out.println(bucket));

The above piece works perfectly. The filter function also returns a stream.

However, with functional programming, we just declare what we want. So lets revert back to this:

buckets.stream()
.filter(bucket -> bucket.getPriority() > 7)
.forEach(bucket -> System.out.println(bucket));

Now, lets look at the forEach block.

The forEach block accepts a Consumer. The Consumer is equally a functional Interface that expects the accept method. This method declares what would be done with each item. In our case, we print each bucket.

@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*
*
@param t the input argument
*/
void accept(T t);
}

Here is our adjusted code with Consumer interface and accept method. The code below works perfectly.

buckets.stream()
.filter(bucket -> bucket.getPriority() > 7)
.forEach(new Consumer<Bucket>(){
@Override
public void accept(Bucket b){
System.out.println(b);
}
});

Let’s revert back to the simplified functional logic to learn about method reference. After reverting this is what we have:

bucketStream
.filter(bucket -> bucket.getPriority() > 7)
.forEach(bucket -> System.out.println(bucket));

So we can see that with lambda expressions, we have shortened and simplified the code base. See an example below:

From

.filter(new Predicate<Bucket>() {
public boolean test(Bucket bucket){
return bucket.getPriority() > 7;
}
})

to

.filter(bucket -> bucket.getPriority() > 7)

and the consumer from

.forEach(new Consumer<Bucket>(){
@Override
public void accept(Bucket b){
System.out.println(b);
}
});

to

.forEach(bucket -> System.out.println(bucket));

So what are Lambda Expressions?

Lambda expressions expose instances of functional interfaces — such as Consumer and Predicate shown above — with a single abstract method.

It was added to Java 8 to provide the following:

  • A function that can be created without belonging to any class.
  • Lambda expressions that can passed around in a chain and used as if it were an object.

The syntax is

lambda operator -> body

Zero operators do not need the parenthesis, so either of these would work.

Without parenthesis:

bucket -> System.out.println(bucket)

With parenthesis:

(bucket) -> System.out.println(bucket)

Now with method reference, we can simplify our code from this(before) to after.

Before

bucketStream
.filter( (bucket) -> bucket.getPriority() > 7)
.forEach( (bucket) -> System.out.println(bucket));

To this:

bucketStream
.filter( (bucket) -> bucket.getPriority() > 7)
.forEach( System.out::println);

We are automatically calling the print method with each value from forEach.

Let’s take it a step higher by abstracting the filter logic to a function and use method reference.

First, let us extract that logic into a method like so:

bucketStream
.filter( (bucket) -> isaAboveNumber(bucket))
.forEach( System.out::println);

Here is the logic in a new method.

private static boolean isaAboveNumber(Bucket bucket) {
return bucket.getPriority() > 7;
}

Next, let apply method reference.

bucketStream
.filter( BucketTestMain::isaAboveNumber)
.forEach( System.out::println);

Because it is static method, we used the class name followed by (::) and method name. By using method reference (::), Java knows that it would pass the argument to the function.

By now, you would have seen how code base can be simplified using functional programming in Java.

The code base will be available on github here:

https://github.com/zero-to-production/functional-programming

--

--

Paul ‘Tofunmi O.
Zero To Production

Passionate about business, technology, abundant living — a life of purpose — and a true follower of Jesus