Using Java Optional Correctly

Java 8 added a new class to handle nullability: java.util.Optional. But many people use it wrongly, or overuse it. This article will give a simple guide how to use Optional correctly.

We can start from a question in Stackoverflow:

Should Java 8 getters return optional type?

Optional type introduced in Java 8 is a new thing for many developers.

Is a getter method returning Optional<Foo> type in place of the classic Foo a good practice? Assume that the value can be null.

Where Brian Goetz gave his answer:

Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.

I think routinely using it as a return value for getters would definitely be over-use.

There's nothing wrong with Optional that it should be avoided, it's just not what many people wish it were, and accordingly we were fairly concerned about the risk of zealous over-use.

Based on above question and answer, coupled with Sonar rules, here some best practices on how to use Optional:

"null" should not be used with "Optional"

The concept of Optional is that it will be used when null could cause errors. In a way, it replaces null, and when Optional is in use, there should never be a question of returning or receiving null from a call.

Noncompliant Code Example

import java.util.Optional;
import javax.annotation.Nullable;

public class NullNoncompliant {

    public void doSomething() {
        Optional<String> optional = getOptional();
        if (optional != null) {  // Noncompliant
            // do something with optional...
        }
    }

    @Nullable // Noncompliant
    public Optional<String> getOptional() {
        // ...
        return null;  // Noncompliant
    }
}
                    

Compliant Code Example

import java.util.Optional;

public class OptionalNullCompliant {

    public void doSomething() {
        Optional<String> optional = getOptional();
        optional.ifPresent( 
            value -> {
                // do something with optional...
            }
        );
    }

    public Optional<String> getOptional() {
        // ...
        return Optional.empty();
    }
}
                    

"Optional" should not be used for parameters

The Java language authors have been quite frank that Optional was intended for use only as a return type, as a way to convey that a method may or may not return a value.

And for that, it's valuable but using Optional on the input side increases the work you have to do in the method without really increasing the value. With an Optional parameter, you go from having 2 possible inputs: null and not-null, to three: null, non-null-without-value, and non-null-with-value. Add to that the fact that overloading has long been available to convey that some parameters are optional, and there's really no reason to have Optional parameters.

Noncompliant Code Example

import java.util.Optional;

public class NotParameterNonCompliant {

    public String sayHello(Optional<String> name) {  // Noncompliant
        if (name == null || !name.isPresent()) {
            return "Hello World";
        } else {
            return "Hello " + name;
        }
    }
}
                    

Compliant Code Example

public class NotParameterCompliant {

    // Yes... don't use Optional at all
    public String sayHello(String name) {
        if (name == null) {
            return "Hello World";
        } else {
            return "Hello " + name;
        }
    }
}
                    

Optional value should only be accessed after calling isPresent()

Optional value can hold either a value or not. The value held in the Optional can be accessed using the get() method, but it will throw a NoSuchElementException if there is no value present. To avoid the exception, calling the isPresent() method should always be done before any call to get().

Alternatively, note that other methods such as orElse(...), orElseGet(...) or orElseThrow(...) can be used to specify what to do with an empty Optional.

Which is in echo with Brian Goetz's public announcement in the same topic:

(Public service announcement: NEVER call Optional.get unless you can prove it will never be null; instead use one of the safe methods like orElse or ifPresent. In retrospect, we should have called get something like getOrElseThrowNoSuchElementException or something that made it far clearer that this was a highly dangerous method that undermined the whole purpose of Optional in the first place. Lesson learned. (UPDATE: Java 10 has Optional.orElseThrow(), which is semantically equivalent to get(), but whose name is more appropriate.))

Noncompliant Code Example

import java.util.Optional;

public class IsPresentNonCompliant {

    public void doSomething() {

        Optional<String> value = this.getOptional();

        // ...
        String stringValue = value.get(); // Noncompliant
    }

    public Optional<String> getOptional() {
        // ...
        return Optional.empty();
    }
}
                    

Compliant Code Example

import java.util.Optional;

public class IsPresentCompliant {

    public void doSomething() {
        Optional<String> value = this.getOptional();

        // ...
        if (value.isPresent()) {
            String stringValue = value.get();
        }

        // or...
        String stringValue = value.orElse("default");
    }

    public Optional<String> getOptional() {
        // ...
        return Optional.empty();
    }
}
                    

The most important point we need to understand about Optional API is NEVER call Optional.get unless we can prove it will never be null. Optional.orElseThrow() is semantically equivalent to get(), but whose name is more appropriate.

On how to access Optional value, we must understand Optional API. Check if the value present or not present in the Optional with:

  • isEmpty(): If a value is not present, returns true, otherwise false.
  • isPresent(): If a value is present, returns true, otherwise false.

When value is present or no value present, what next?

  • ifPresent​(...): If a value is present, performs the given action with the value, otherwise does nothing.
  • or​(...): if a value is present, returns an Optional describing the value, otherwise returns an Optionalproduced by the supplying function.
  • orElse​(...): If a value is present, returns the value, otherwise returns other.
  • orElseGet​(...): If a value is present, returns the value, otherwise returns the result produced by the supplying function.
  • orElseThrow(): If a value is present, returns the value, otherwise throws NoSuchElementException.
  • orElseThrow​(...): If a value is present, returns the value, otherwise throws an exception produced by the exception supplying function.

Avoid Use Optional As Property in Java Bean

Actually this is one of the rule of thumb that quite often voided by Java Programmer (including myself and some samples in this site). The issue is, Optional class is definitively not intended for use as a property of a Java Bean. Optional does not implement Serializable, which is generally necessary for widespread use as a property of an object.

Use OptionalDouble, OptionalInt, or OptionalLong instead of Generic Optional

Java created class OptionalDouble, OptionalInt, and OptionalLong as wrappers for primitive types double, int, and long. The use of these classes are preferred for accessing their respective primitive types instead of using generic Optional class

Noncompliant Code Example

import java.util.Optional;

public class PrimitiveNonCompliant {

    public void doSomething() {        
        Optional<Double> d = Optional.of(90.09d);
        Optional<Integer> i = Optional.of(888);
        Optional<Long> l = Optional.of(2147483748l);
    }
}
                    

Compliant Code Example

import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;

public class PrimitiveCompliant {

    public void doSomething() {        
        OptionalDouble d = OptionalDouble.of(90.09d);
        OptionalInt i = OptionalInt.of(888);
        OptionalLong l = OptionalLong.of(2147483748l);
    }
}