Java 8: Take your abstractions to the next level

One of the many reasons I enjoy working with functional programming, is the high level of abstraction. You'll end up with more readable and concise code that is closer to the business logic.

In this post I'll look at four things that will help you get your abstractions to the next level using the functional features introduced in Java 8.

1. No more loops

I've said it before and I'll say it again. Say goodbye to the loops and welcome the Stream API. The days of telling Java how to loop elements has come to an end. With the Stream API, Java is letting you know that it got the how part covered, letting you focus on the what.

Let's consider the following scenario.

We have a list of articles, and each article has a list of tags. We now want to get the first article containing a Java tag.

Let's first look at a traditional solution using the loop.

public Article getFirstJavaArticle() {

    for (Article article : articles) {
        if (article.getTags().contains("Java")) {
            return article;

    return null;

Let's now trust Java with the responsibility of iterating our list by using the Stream API.

public Optional<Article> getFirstJavaArticle() {  
        .filter(article -> article.getTags().contains("Java"))

Pretty cool right?

Using the higher-order function filter, we can simply use a predicate to define a valid article. findFirst will then return the first occurrence of an article that matches our predicate.

You may ask why we should filter the whole list, when we only want the first match. Well, since streams are lazy and filter returns a stream, this approach only processes elements until it finds the first match.

I've earlier dedicated a post to the topic of replacing your loops with the higher level stream API, so read that one if you want more examples.

2. Clean up your null checks

You may noticed that we returned something called Optional<Article> in the previous example.

Optional is a container object that may or may not contain a non-null value.

This object has some useful higher-order functions, removing the concern of adding repeating if null/notNull checks allowing us to focus on what we want done.

Let's start of by taking a step out of our getFirstJavaArticle method. Let's now say that if we don't find a Java article, we're happy with the latest article we got.

Let's take a look at how a typical solution would be.

    Article article = getFirstJavaArticle();

    if (article == null) {
        article = fetchLatestArticle();

Let's take a look at the solution using Optional<T>.


How great doesn't that look?

No variable declarations, no if conditions, no mention of null. We simply use Optional.orElseGet to say what we want done if no value is found.

Let's do another example on Optional. Let's say that we want the title of the first Java article if it's found.

Again, typically we would have a null check here, but do you know what? Optional is here to save the day.


As you can see, Optional implements the higher-order function map, helping us apply a function to the result if it's present.

For more on Optional check out this.

3. Create your own higher-order function

As we've seen, Java 8 comes with a range of helpful higher-order functions and you can do wonders with them, but why stop there? Why not create your own?

The only thing you have to do to make a higher-order function, is to take one of Java's functional interfaces or a SAM-type interface as an argument and/or return one of them.

To illustrate this, let's consider the following scenario.

We have a printer that can print different types of documents. The printer has to warm up before printing and go in sleeping mode after.

We now want to be able to send instructions to the printer without having to worry about the startup and shutdown routine. This can be solved by creating a higher-order function.

public void print(Consumer<Printer> toPrint) {


As you can see, we take Consumer<Printer>, which is one of the functional interfaces, as an argument. We then execute this function as a step between the startup and shutdown routine.

Now we can easily use our printer without thinking about anything else than what we want to print.

// Print one article
printHandler.print(p -> p.print(oneArticle));

// Print several articles
printHandler.print(p -> allArticles.forEach(p::print));  

For a more detailed example on creating your own higher order functions, check out my post on how to create a TransactionHandler.

4. The danger of duplication. Stay DRY!

Writing functions are easy and fast. However, with easy written code comes the desire to duplicate.

Let's recap the first example in the post.

public Optional<Article> getFirstJavaArticle() {  
        .filter(article -> article.getTags().contains("Java"))

This method served us well, but it doesn't cut it any more. We want to be able to find articles based on other tags and on other requirements in general.

It would be tempting to just create new streams. They are so small and simple to make, so how could that hurt?

On the contrary, the small pieces of code should be a motivation to take the DRY principle further.

Let's start to pull the pieces apart and make our code support these extra scenarios.

First, let's refactor our getFirstJavaArticle to be a generic higher-order function where we can send in the predicate defining a valid article.

public Optional<Article> getFirst(Predicate<Article> predicate) {  

Let's now use this function to fetch a few different articles.

getFirst(article -> article.getTags().contains("Java"));  
getFirst(article -> article.getTags().contains("Scala"));  
getFirst(article -> article.getTitle().contains("Clojure"));  

Still we have some duplicated code. You can see that we have two predicates that checks the tag values. Let's try remove this duplication with the help of the Function interface.

Function<String, Predicate<Article>> basedOnTag = tag -> article -> article.getTags().contains(tag);


Great! I would say that this code now stays quite true to the DRY principle, wouldn't you?

I hope this post was helpful and gave a few ideas on what you could do to put your abstractions on a higher level using Java 8 and it's functional features.