Java 8 Optional Usage and Best Practices

Hopewell Mutanda
7 min readMay 28, 2019

--

Introduction

According to Oracle documentation an Optional is a container object which may or may not contain a non-null value. It was introduced in Java 8 to cure the curse of NullPointerExceptions. In essence Optional is a wrapper class which contains a reference to some other object. In this context an object is just a pointer to a memory location and it can as well point to nothing. Another way to look at it is as a way to provide a type-level solution for representing optional values instead of null references.

Life before optional

Before Java 8, programmers would return null instead of Optional . There were a few

shortcomings with this approach. One was that there wasn’t a clear way to express that null might be a special value. By contrast, returning an Optional is a clear statement in the API that there might not be a value in there. If we wanted to make sure that we wont get a null pointer exception then we would need to do explicit null check for each reference as show below and we all agree that’s a lot of boilerplate.

// Life before Optional
private void getIsoCode( User user){
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
}

To ease this process, let’s take a look at how we can use the Optional class instead, from creating and verifying an instance, to using the different methods it provides and combining it with other methods that return the same type, the latter being where the true power of Optional lies.

Features of optional

The optional class provides around 10 methods which we can use for creating and using the Optional class and we are going to see how they are used below.

Creating an optional

The are three creational methods for creating an optional instance.

1. static <T> Optional<T> empty()

Returns an empty Optional instance.

// Creating an empty optional
Optional<String> empty = Optional.empty();

Returns an empty {Optional} instance. No value is present for this Optional. Though it may be tempting to do so, avoid testing if an object is empty by comparing with {==} against instances returned by {Option.empty()}. There is no guarantee that it is a singleton. Instead, use {isPresent()}.

2. static <T> Optional<T> of(T value)

Returns an Optional with the specified present non-null value.

// Creating an optional using of
String name = "java";
Optional<String> opt = Optional.of(name);

The static method of expects a non-null argument otherwise it will throw a nullpointer exception. So what if we dont know if the argument will be null or not thats when we use ofNullable which is described below.

3. static <T> Optional<T> ofNullable(T value)

Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.

// Possible null value
Optional<String> optional = Optional.ofNullable(name());

private String name(){
String name = "Java";
return (name.length() > 5) ? name : null;
}

By doing this, if we pass in a null reference, it doesn’t throw an exception but rather returns an empty Optional object:

Ok so those are the three methods of creating optionals either dynamically or manually. The next set of methods is for checking value presence.

1. boolean isPresent()

Return true if there is a value present, otherwise false. Meaning to returns true if the contained object is not null and false otherwise. This method is normally called first on an optional before doing any other operations on the object.

//ispresent
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
//Do something, normally a get
}

2. boolean isEmpty()

Return false if there is a value present, otherwise true. This does the opposite of isPresent and is only available in Java 11 and above.

//isempty
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isEmpty()){
//Do something
}

3. void ifPresent(Consumer<? super T> consumer)

If a value is present, invoke the specified consumer with the value, otherwise do nothing.

If you are new to Java 8 then you are probably wondering what is a consumer, well in simple terms a consumer is a method which accepts an argument and does not return anything. When using ifPresent this looks like killing two birds with one stone. We can do a value presence check and perform the intended operation with one method as show below.

//ifpresent
Optional<String> optional1 = Optional.of("javaone");
opt.ifPresent(s -> System.out.println(s.length()));

The optional class provides another set of methods for getting a value in an optional.

1. T get()

If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException. After all is said and done, what we want is the value that is stored in the Optional and we can get it by simply calling get(). However this method throws an exception when the value is null, thats when the orElse() method comes to the rescue.

//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
String value = optional1.get();
}

2. T orElse(T other)

Return the value if present, otherwise return other.

The orElse() method is used to retrieve the value wrapped inside an Optional instance. It takes one parameter which acts as a default value. The orElse() method returns the wrapped value if it’s present and its argument otherwise:

//orElse
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("default_name");

As if that is not enough the optional class goes on to provide another method of getting a value even if its null called orElseGet()

3. T orElseGet(Supplier<? extends T> other)

Return the value if present, otherwise invoke other and return the result of that invocation.

The orElseGet() method is similar to orElse(). However, instead of taking a value to return if the Optional value is not present, it takes a supplier functional interface which is invoked and returns the value of the invocation:

//orElseGet
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");

So what is the difference between orElse() and orElseGet()

At first glance, it might seem as if the two methods have the same effect. However, this is not exactly the case. Let’s create some examples that highlight the similarity as well as the difference in behavior between the two.

First, let’s see how they behave when an object is empty:

String text = null;

String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);


defaultText = Optional.ofNullable(text).orElse(getDefaultValue());


public String getDefaultValue() {
System.out.println("Getting Default Value");
return "Default Value";
}

In the above example, we wrap a null text inside an Optional object and we attempt to get the wrapped value using each of the two approaches. The side effect is as below:

Getting default value...
Getting default value...

The default method is called in each case. It so happens that when the wrapped value is not present, then both orElse() and orElseGet() work exactly the same way.

Now let’s run another test where the value is present and ideally, the default value should not even be created:

String text = "Something";System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);

System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());


public String getDefaultValue() {
System.out.println("Getting Default Value");
return "Default Value";
}

In the above example, we are no longer wrapping a null value and the rest of the code remains the same. Now let’s take a look at the side effect of running this code:

Using orElseGet:
Using orElse:
Getting default value...

Notice that when using orElseGet() to retrieve the wrapped value, the deafult method is not even invoked since the contained value is present.

However, when using orElse(), whether the wrapped value is present or not, the default object is created. So in this case, we have just created one redundant object that is never used.

In this simple example, there is no significant cost to creating a default object, as the JVM knows how to deal with such. However, when a method such as deafult has to make a web service call or even query a database, then the cost becomes very obvious.

Best practices of using optional

Just like any other feature of a programming langauge it can be used correctly or it can be abused. In order to know the best way to use the optional class one needs to understand the following

1. What is it trying to solve

Optional is an attempt to reduce the number of null pointer exceptions in Java systems, by adding the possibility to build more expressive APIs that account for the possibility that sometimes return values are missing.

If Optional was there since the beginning, most libraries and applications would likely deal better with missing return values, reducing the number of null pointer exceptions and the overall number of bugs in general.

2. What is it not trying to solve

Optional is not meant to be a mechanism to avoid all types of null pointers. The mandatory input parameters of methods and constructors still have to be tested for example.

Like when using null, Optional does not help with conveying the meaning of an absent value. In a similar way that null can mean many different things (value not found, etc.), so can an absent Optional value.

The caller of the method will still have to check the javadoc of the method for understanding the meaning of the absent Optional, in order to deal with it properly.

Also in a similar way that a checked exception can be caught in an empty block, nothing prevents the caller of calling get() and moving on.

3. When to use it

The intended use of Optional is mainly as a return type. After obtaining an instance of this type, you can extract the value if it’s present or provide an alternate behavior if it’s not.

One very useful use-case of the Optional class is combining it with streams or other methods that return an Optional value to build fluent APIs. See code snippet below

User user = users.stream().findFirst().orElse(new User("default", "1234"));

4. When not to use it

a) Do not use it as a field in a class as it is not serialisable

If you do need to serialize an object that contains an Optional value, the Jackson library provides support for treating Optionals as ordinary objects. What this means is that Jackson treats empty objects as null and objects with a value as fields containing that value. This functionality can be found in the jackson-modules-java8 project.

b) Do not use it as a parameter for constructors and methods as it would lead to unnecessarily complicated code.

User user = new User("john@gmail.com", "1234", Optional.empty());

Important to note

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

--

--