Introduction

When Java 8 came and I learned about Optional, I was quite excited and started to use it everywhere for denoting absence of value. But I was quite surprised to read the API Note from Java 9 docs.

Optional is primarily intended for use as a method return type where there is a clear need to represent “no result,” and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

So Optional must be used as only return value. You shouldn’t accept Optional as method parameter. Let’s explore the pitfalls and benefits in details.

Optional Misuse

Everybody had their own preference about programming and majority times perfect way of coding is just a matter of taste. But in my experience if you use the API the way developer intended, there is less surprises. All misuse of Optional boils down to 3 cases:

  • Property
  • Getter
  • Method Parameter

Property

You can use Optional to denote null-able properties like below:

public class Person {
    private Optional<String> additionalPhoneNumber;

    // getters and setters
}

But problem with this is java.util.Optional doesn’t implement java.io.Serializable as it wasn’t intended to use as property. So when you need to serialize data Optional property won’t be serialized. Although modern libraries and frameworks can change this behaviors using reflection or byte code generation, but you have to depend on that specific library. Mixing multiple libraries with different philosophy (Optional property Serializable vs not Serializable) can make the code complicated.

Getter

This is maybe tempting to declare nullable property normally and add a getter with Optional. You are not violating any rule as documentation says it is only intended to be used as return type.

public class Person {
    private String additionalPhoneNumber;

    public Optional<String> getAdditionalPhoneNumber() {
        return Optional.ofNullable(this.additionalPhoneNumber);
    }
}

This code will work perfectly but tools like IDE or some code generator won’t recognize this as getter. Java tools like JSP EL expression depend heavily on naming convention. Those don’t support Optional getters (at least not yet). You can declare another getter but that will also make code unnecessarily complicated.

Method Parameter

Final misuse is to use Optional as method parameter. To be honest I find following code more accessible then mentioning in documentation:

public void printGreetings(String name, Optional<String> salutation) {
    String salutaionText = salutation.orElse("");
    System.out.printf("Hello %s, Welcome!\n", salutationText + name);
}

First problem is calling this method now becomes a bit difficult. We can’t call with null and always have to wrap in Optional.

// Will throw NullPointerException
printGreetings("John", null);

// Need to wrap with Optional even we have non-empty String
printGreetings("John", Optional.of("Mr"));

Another disadvantage using Optional here is Java tools already have better support for solving this problem. Modern IDEs and libraries like Lombok has support for annotation which can make code more readable.

import lombok.NotNull

public void printGreetings(@NotNull String name, String salutation) {
    String salutaionText = Objects.nonNull(salutation) ? salutation : "";
    System.out.printf("Hello %s, Welcome!\n", salutationText + name);
}

Optional Usage

To take the full advantage of Optional it is mandatory to learn it’s API and understand how it is intended to be used. Let’s first see how you can create Optional objects.

import java.util.Optional;

// This will create an Optional with no value (Equivalent of null)
Optional<String> emptyTitle = Optional.empty();

// Use Optional.of() when you hava non-null value
Optional<String> titleOptional = Optional.of("Mr."); 
Optional<String> titleOptional = Optional.of(null); // Will throw NullPointerException

// Use Optional.ofNullable() when value can be null
Optional<String> titleOptional = Optional.ofNullable("Mr.");
Optional<String> titleOptional = Optional.ofNullable(null); // Will create Optional.empty() object;

Now for getting the value from Optional we have following method:

Optional<String> nameOptional = Optional.of("John");

String name = nameOptional.get();
System.out.println(name); // John

But we won’t always know weather Optional has value or is empty. Following methods can query for existence of value:

if (nameOptional.isPresent()) {
    System.out.println(nameOptional.get());
}

// Same code in functional style
nameOptional.ifPresent(System.out.println);

Following methods are used when Optional doesn’t have value and we default value for wants to throw exception

Optional<String> loggedInUserOptional = Optional.empty();

String loggedInUserName = loggedInUserOptional.orElse("Guest");
System.out.println(loggedInUserName); // Guest

// Similar to orElse() but takes a java.util.function.Supplier
String loggedInUserName = loggedInUserOptional.orElseGet(() -> "Guest " + getGuestNumber());
System.out.println(loggedInUserName); // Guest 1, when getGuestNumber() returns 1

// Following code will throw IllegalStateException immediately if optional is empty
loggedInUserOptional.orElseThrow(IllegalStateException::new);

Filter is used to test for condition. It takes a java.util.function.Predicate and returns empty Optional if condition doesn’t met.

Optional<User> user = findUserById(id);

if (author != null && user.isActive()) {
    System.out.println("Name: " + user.getName());
}

// Same code using filter()
user.filter(User::isActive).ifPresent(user -> {
    System.out.println("Name: " + user.getName());
});

These methods helps to manipulate value inside Optional. Both of them are similar but takes different kind of java.util.funciton.Function. Suppose I want to makeOptional<String> uppercase both map() or flatMap() can be used here.

Optional<String> nameOptional = Optional.of("John");

// Here map() takes [String -> Something] function 
nameOptional.map(name -> name.toUpperCase()).ifPresent(System.out::println); // JOHN

// flatMap() takes [String -> Optional<Something>] function
// Same code written using flatMap()
nameOptional.flatMap(name -> Optional.of(name.toUpperCase())).ifPresent(System.out::println); // JOHN

Resource