Optional usages, defaults and checks

Featured image

We have three ways to create an instance of Optional

var opt = Optional.of("notNull")

not null otherwise NullPointerException

var opt = Optional.ofNullable(mightBeNull)

you don’t know either it will contain null or not.

var opt = Optional.empty()

no value, similar concept to null, but without NullPointerException

Defaults with Optional

For the below case we would like to search our database for the customer if we don’t find it we would like to search our legacy database/service if we failed here as well let’s return default customer, throw exception or something else. Maybe return some error_code to the frontend and create popup for the user.

Optional<Customer> defaultCustomer = Optional.of(new Customer("John Doe"));

Optional<Customer> customer = getCustomer()
                .or(() -> getLegacyCustomer())
                .or(() -> defaultCustomer);

Optional with checks

We need to do some checks on data and that leads to lots of IFs:

public User getUser(UserId userId) {
  User user = userRepo.get(userId);
  if(user == null){
    throw new UserDoesNotExistsExecption();
  }
  return user;
}

Now, with Optional we have a bit more concise code:

public User getUser(UserId userId) {
    return userRepo.find(userId)
        .orElseThrow(UserDoesNotExistsExecption::new);
}

Let’s develop our code further, few more changes. Now we would like to have user payment details and check if the user paid his subscription.

public UserPaymentDetails getUserPaymentDetails(UserId userId) {
    User user = getUser(userId)
    if(user.getPaymentDetails() == null && !user.isSubscribtionActive) {
        throw new UserPaymentNotFoundException();
    }
    return user.getPaymentDetails();
}

Continuing as before with Optional we have more concise and readable code:

public UserPaymentDetails getUserPaymentDetails(UserId userId) {
    return userRepo.find(userId)
        .filter(User::isSubscribtionActive)
        .flatMap(User::getPaymentDetails)
        .orElseThrow(UserPaymentNotFoundException::new);
}

For me the biggest benefit of using Optional is readability (if used properly) and you can avoid NullPointerException, because you have a bit easier time to control flow of the application with FP.