1. Introduction

In this tutorial, we’ll see how to throw a custom exception when an Optional is empty.

2. Optional.orElseThrow() Method

Simply put, if the value is present, then isPresent() returns true, and calling get() returns this value. Otherwise, it throws NoSuchElementException.

There’s also a method orElseThrow(Supplier<? extends X> exceptionSupplier) that allows us to provide a custom Exception instance. This method returns value only if it’s present. Otherwise, it throws an exception created by a provided supplier.

3. Throw Exception When Value Is Missing

Let’s say we have a method which returns a nullable result:

public String findNameById(String id) {
    return id == null ? null : "example-name";
}

Now we’ll call our findNameById(String id) method twice and wrap the result with an Optional by using the ofNullable(T value) method.

Optional provides a static factory method for creating new instances. This method is called ofNullable(T value). Then we can call orElseThrow().

We can verify the behavior by running this test:

@Test
public void whenIdIsNull_thenExceptionIsThrown() {
    assertThrows(InvalidArgumentException.class, () -> Optional
      .ofNullable(personRepository.findNameById(null))
      .orElseThrow(InvalidArgumentException::new));
}

According to our implementation, findNameById() returns null. So InvalidArgumentException will be thrown from the orElseThrow() method.

We can call this method with a non-null argument. Then, we won’t get an InvalidArgumentException:

@Test
public void whenIdIsNonNull_thenNoExceptionIsThrown() {
    assertAll(() -> Optional
      .ofNullable(personRepository.findNameById("id"))
      .orElseThrow(RuntimeException::new));
}

4. Throw Exception When Value Is Present

As we mentioned earlier, orElseThrow() returns the wrapped value if it’s present, otherwise it throws the supplied exception.

Now, let’s see how we can throw an exception when the contained value is present.

4.1. Optional.ifPresent()

In short, this method allows us to execute a specific code when the value of the Optional is not null.

For example, let’s see how we can use this method to throw a custom exception if we find a User with a specific id.

First, let’s consider the UserFoundException exception:

public class UserFoundException extends RuntimeException {

    public UserFoundException(String message) {
        super(message);
    }

}

The basic idea here is to throw UserFoundException when the findById() method returns an Optional with a non-null User object.

Next, we’re going to create another method that uses the ifPresent() method of Optional to throw our custom exception UserFoundException:

public void throwExceptionWhenUserIsPresent(String id) {

    this.findById(id)
      .ifPresent(user -> {
          throw new UserFoundException("User with ID: " + user.getId() + " is found");
      });

}

Now, let’s create a test case to confirm that our custom exception is thrown when the wrapped User is present:

@Test
void givenExistentUserId_whenSearchForUser_thenThrowException() {

    final UserRepositoryWithOptional userRepositoryWithOptional = new UserRepositoryWithOptional();
    String existentUserId = "2";

    assertThrows(UserFoundException.class, () -> userRepositoryWithOptional.throwExceptionWhenUserIsPresent(existentUserId));

}

We also need to make sure that UserFoundException isn’t thrown if there’s no User to return:

@Test
void givenNonExistentUserId_whenSearchForUser_thenDoNotThrowException() {

    final UserRepositoryWithOptional userRepositoryWithOptional = new UserRepositoryWithOptional();
    String nonExistentUserId = "8";

    assertDoesNotThrow(() -> userRepositoryWithOptional.throwExceptionWhenUserIsPresent(nonExistentUserId));

}

The sole limitation of this approach is that we lose the contained User – ifPresent() accepts a Consumer which returns nothing.

5. Conclusion

In this quick article, we discussed how to throw an exception from Java 8 Optional. 

As always, the the source code for the examples is available over on GitHub.