DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Java
  4. The Beauty of Java Optional and Either

The Beauty of Java Optional and Either

Many Java developers often overlook its functional programming capabilities. Learn how to chain Optional and Either to write concise and beautiful code.

Sanjay Patel user avatar by
Sanjay Patel
CORE ·
Feb. 15, 23 · Tutorial
Like (5)
Save
Tweet
Share
9.95K Views

Join the DZone community and get the full member experience.

Join For Free

Many of us Java developers — particularly beginners — often overlook its functional programming capabilities. In this article, we'd look at how to chain Optional and Either to write concise and beautiful code.

To illustrate, let's assume we have a bank where a user could have zero or more accounts. The entities look as below:

Java
 
record User(
   int id,
   String name
) {}

record Account(
    int id,
    User user // a user can have zero or more account
) {}


For fetching the entities, the repositories look as below:

Java
 
interface UserRepository {
    Optional<User> findById(int userId);
}

interface AccountRepository {
    Optional<Account> findAnAccountOfUser(User user); // given a user, find its any account if it has one
}


Now, get ready for a couple of assignments!

First Assignment

Let's code a method Optional<Account> findAnAccountByUserId(Integer userId) that would:

  1. Given a userId, return any one account of the user, if there's one
  2. If either there's no user with the given id, or there's no account of the user, return an empty Optional

A novice solution could be as follows:

Java
 
    public Optional<Account> findAccountByUserId(int userId) {

        Optional<User> possibleUser = userRepository.findById(userId);
        if (possibleUser.isEmpty())
            return Optional.empty();
        var user = possibleUser.orElseThrow();
        return accountRepository.findAnAccountOfUser(user);
    }


But, then the map method of Optional strikes our mind! Instead of checking for possibleUser.isEmpty(), we could just map the user, if present, to an account:

Java
 
    public Optional<Account> findAccountByUserId(int userId) {
        return userRepository
                .findById(userId)
                .map(accountRepository::findAnAccountOfUser);
    }


We land up with a compilation error because accountRepository.findAnAccountOfUser(user) returns an Optional<Account>, whereas the map method above needs an Account. For this exact use case, Optional provides a flatMap method, which flattens nested Optionals. So, changing map to flatMap would work.

Java
 
    public Optional<Account> findAccountByUserId(int userId) {
        return userRepository
                .findById(userId)
                .flatMap(accountRepository::findAnAccountOfUser);
    }


Cool! Get ready for a more complex assignment.

Second Assignment

When a user/account is not found, instead of returning an empty optional, how about indicating exactly what was not found: user or account?

We could approach this problem in a few ways:

Throw Exceptions

We could define some custom exceptions, viz.  UserNotFoundException and AccountNotFoundException, and throw those:

Java
 
    public Account findAccountByUserIdX(int userId) {
        var user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
        return accountRepository.findAnAccountOfUser(user).orElseThrow(AccountNotFoundException::new);
    }


However, using exceptions for expected cases is considered an anti-pattern: Googling will get you numerous articles about the subject. So let's avoid that.

Use a Result Interface

Another approach would be returning a custom Result object instead of returning Optional; i.e., Result findAnAccountByUserId(Integer userId). The result would be an interface that would be implemented by custom error classes, as well as Account and User.

Use Either

A third approach, which I find simpler,  is to return an Either instead of Optional. Whereas an Optional holds zero or one value, an Either holds either of two values. It's typically used to hold either an error or a success value.

Unlike Optional, you don't get a Java Either implementation out of the box. There are quite a few libraries. I prefer using jbock-java/either because it's lightweight and simple.

So, let's first define the error interface and classes:

Java
 
    interface Error {}
    record UserNotFound() implements Error {}
    record AccountNotFound() implements Error {}


Let's now attempt coding:

Java
 
public Either<Error, Account> findAccountByUserId(int userId) {
  ...
}


Did you notice above that we used Error as the left generic parameter, whereas Account as the right one? That wasn't accidental: the convention when using Either is that the left is used for errors whereas the right is used for success values. 

Either has a similar functional API like Optional. For example, we have map and flatMap for mapping the success values, whereas we have mapLeft and flatMapLeft for mapping the errors. We also have utility methods like Either.left(value) and Either.right(value) to create Either objects. Do have a look at its API. It has many interesting features for functional programming.

So, continuing our journey, we could first create an either having the user or error as below:

Java
 
public Either<Error, Account> findAccountByUserId(int userId) {
    var eitherErrorOrUser = userRepository
                .findById(userId)
                .map(Either::<Error, User>right)
                .orElse(Either.left(new UserNotFound()))
    ...
}


Lines 4 and 5 above convert an Optional<User> to Either<UserNotFound, User>. Because converting an Optional to an Either would be a common use case, let's code a utility method for it:

Java
 
public class EitherUtils {

  public static <L, R>  Either<L, R> of(Optional<R> possibleValue, Supplier<L> errorSupplier) {
        return possibleValue.map(Either::<L, R>right).orElseGet(() -> Either.<L, R>left(errorSupplier.get()));
    }
}


It takes the optional and an errorSupplier. The errorSupplier is used for composing the error if the optional is empty.

Using it, our code now looks like this:

Java
 
public Either<Error, Account> findAccountByUserId(int userId) {
    var eitherErrorOrUser = EitherUtils
        .<Error, User>of(userRepository.findById(userId), UserNotFound::new)
    ...
}


Next, as above, eitherErrorOrUser could be mapped for an account in a similar way. The complete solution would then look like this:

Java
 
    public Either<Error, Account> findAccountByUserId(int userId) {
        return EitherUtils
                .<Error, User>of(userRepository.findById(userId), UserNotFound::new)
                .flatMap(user -> of(accountRepository.findAnAccountOfUser(user), AccountNotFound::new));
    }


Looks cute, doesn't it?

Summary

Consider using functional programming capabilities of Java wherever possible, and make your code cute and concise!

Functional programming Java (programming language) Data Types

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Chaos Engineering Tutorial: Comprehensive Guide With Best Practices
  • Assessment of Scalability Constraints (and Solutions)
  • Top 5 Data Streaming Trends for 2023
  • Solving the Kubernetes Security Puzzle

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: