avatarViraj Shetty

Summary

This article discusses the use of the Optional.stream() method in Java, focusing on its integration into stream pipelines for functional and fluent style programming.

Abstract

The Optional.stream() method in Java allows developers to create a stream from an Optional object, which can be empty or contain a single element. This method is useful when integrating Optional objects into Java stream pipelines, as it enables the manipulation of collections using functional programming. The article explains the Optional.stream() method's usage, demonstrating its application in a real-world scenario involving a User class with name and address attributes. It provides examples of how to display all user addresses using functional programming with and without the Optional.stream() method.

Bullet points

  • Optional.stream() is a method introduced in Java 8 that allows the creation of a stream from an Optional object.
  • An Optional object can either have a value or be empty, making it an immutable container for a returned object.
  • Optional objects can be integrated into Java stream pipelines using the Optional.stream() method.
  • Optional.stream() is particularly useful when working with collections, as it allows for functional and fluent style programming.
  • The Optional.stream() method is used in conjunction with the flatMap() method of the Stream interface to create a stream of elements from an Optional object.
  • The flatMap() method flattens a "stream of streams" into a single "stream of elements," making it easier to work with collections in functional programming.
  • The article provides examples of how to display all user addresses using functional programming, with and without the Optional.stream() method.
  • Using Optional.stream() can make the process of displaying user addresses more concise and readable.
  • The article also links to additional resources for learning about the Optional class in Java and its functional programming capabilities.

When to use Optional.stream()

Learn to integrate Java Optional into a stream Pipeline

Photo by Christopher Gower on Unsplash

Java Optional

Optional class was introduced in JDK 8, as a means to reduce the occurrence of NullPointerException in Java. At the most basic level, you can think about a Java Optional class as an immutable (which cannot be changed) container for the actual object that needs to be returned by a method.

The big idea here is that if a method returns a value which can be null, then that method could instead return an Optional with the value contained inside it. In other words, the method will not return a null but an Optional object which has a valid value or might be empty.

Typically a programmer who is developing a Java library, may use Optional types in the method return signature to convey to the caller about the possibility of returning null value.

One way to create an Optional object is to use the ofNullable(..) method as shown below. Here variable optUser is an Optional object of parameterized type User which can contain a valid User object or empty.

Optional<User> optUser 
       = Optional.ofNullable(someUser);

Additionally, the Optional class has a number of methods to extract the value from it. Methods like isPresent() and get() can be used with Imperative style programming. However, particularly interesting are the methods to handle the Optional value in a functional and fluent style. Here are some examples.

ifPresent(..), map(), filter(), flatMap(), or(), orElse(), stream()

One method among them — Optional.stream() — seems a little bizarre at first glance. After all, an Optional object either has a value or does not have a value. What on earth is the point of creating a stream out of it ?

Let’s analyze that a bit more.

Optional.stream()

Java stream programming has been very popular among Java developers. It encourages a functional style of programming and makes the developers job easier in many use cases which deal with Collections.

Here’s a simple example of a Java stream pipeline, where a list of names who have valid addresses are extracted from a list of Person objects.

List<Person> people = getPeople();
List<String> names 
    =  people.stream()
         .filter(Person::isAddressNotNull)
         .map(Person::name)
         .toList();

Such manipulation of collections are extremely straightforward using Java streams and it usually involves using the map() and filter() methods of the Stream interface.

Just like we can create a stream out of the Collection, we can also create a stream out of an Optional object. That’s exactly what the Optional.stream() method is about. When the stream() method is called on a Java Optional, it will return an empty stream if the Optional is empty and it will return a stream of one element if the Optional has a value. Essentially, we can think about an Optional as a collection of 0 or 1 element — even though it does not actually implement the Collection interface.

You might be wondering when such a stream can be useful. Let me show you where Optional.stream() method can be used effectively.

Integrate Optional into Java stream

Consider a User class which has the following attributes — name and address. Furthermore, assume that the developer returns the Optional type for the address — since it can be null.

public class User {
    private final String name;
    private final String address;

    User(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public Optional<String> getAddress() {
        return Optional.ofNullable(address);
    }
}

We have a factory class called Users which has a method called Users.getAllUsers(..) which returns all users in the system.

public class UserFactory {
   public static List<User> getAllUsers() {

      // Fake users - not important
      User user1 = new User("John Doe", "Junk St, MD");
      User user2 = new User("Jane Doe", "Test str, VA");
      User user3 = new User("Jack Doe", null);
      return List.of(user1, user2, user3);
   }
}

Let’s implement the use case to display all the user addresses if they are available (not null) — very simple. We could do this in a functional way as follows.

Users.getAllUsers()
     .stream()
     .map(User::getAddress)
     .filter(Optional::isPresent)
     .map(Optional::get)
     .forEach(System.out::println);

// Output of above code
Junk St, MD
Test str, VA

We use the map(..) method to get a “stream of Optional<String>” — remember that the getAddress(..) returns an Optional<String>. We use the filter(..) method to only look at addresses which are “present” and then again use the map(..) to extract the address (once we know it cannot be null)

This works !

However, a shorter way of doing the same thing is to use the Optional.stream() method along with the flatMap(..) method of Stream interface.

Here’s how to use the Optional.stream(..) method.

Users.getAllUsers()
     .stream()
     .map(User::getAddress)
     .flatMap(Optional::stream)
     .forEach(System.out::println);

// Output of above code
Junk St, MD
Test str, VA

In this case, you see that after using the map(..) method to get a stream of Optional addresses, we use the flatMap(..) method. When we pass the method reference Optional::stream to flatMap(..), flatMap will internally call it on the Optional<String> object available from the previous map(..) method — and this results in a “stream of string”.

Note that — we cannot call map(..) instead of flatMap(..) here because otherwise the output will be “stream of stream of String”. The call to flatMap(..) will essentially flatten the “stream of stream of String” to “stream of String” making life simpler for us.

You can also combine map(..) and flatMap(..) as below — instead of using Optional method references. You might find this easier to understand.

Users.getAllUsers()
     .stream()
     .flatMap(u -> u.getAddress().stream())
     .forEach(System.out::println);

// Output of above code
Junk St, MD
Test str, VA

Note that the map(..) and flatMap(..) methods are called on the stream and not on the Optional object.

Hope this gives you a good idea of when Optional.steam() can be used !

You may also want to check out my two other Medium articles on Java Optional which explains the basics as well as the Functional and Fluent style of development using Optional.

If this post was helpful, please click the clap 👏 button below a few times to show your support. Thanks for reading !

I regularly upload videos to my YouTube Channel at https://www.youtube.com/@viraj_shetty

Also check out my discounted Udemy Courses at https://www.mudraservices.com/courses/

Summary

Many of the Java libraries return Optional types as part of their method calls. So, it is important that these Optional objects be integrated into the Java stream Pipeline with minimal effort.

Optional.stream() method makes this integration easy.

Java
Java Optional
Functional Programming
Recommended from ReadMedium