Guide to Java 8 Optional

All of us must have encountered NullPointerException in our applications. It happens when you try to use an object that has not been initialized, initialized with null or does not point to any instance. In simple words, NULL simply means ‘absence of a value’.

In this Java tutorial, we will discuss one of Java 8 features i.e. Optional that are recommended to minimize the issues occurred when null is used.

1. What is the Type of null?

In Java, we use a reference type to gain access to an object, and when we don’t have a specific object to make our reference point to, then we set such references to null to imply the absence of a value.

The use of null is so common that we rarely put more thought into it. For example, field members of objects are automatically initialized to null, and programmers typically initialize reference types to null when they don’t have an initial value to give them. In general, null is used everytime where we don’t know, or we don’t have a value to give to a reference.

In Java null is actually a type, a special one. It has no name so we cannot declare variables of its type or cast any variables to it; in fact there is only a single value that can be associated with it (i.e. the literal null). Remember that unlike any other types in Java, a null reference can be safely assigned to any other reference types without any error (See JLS 3.10.7 and 4.1).

2. Problems when using null?

Generally, the API designers put the descriptive java docs in APIs and mention there that API can return a null value. The problem is that the caller of the API might have missed reading the javadoc for any reason, and forgotten about handling the null case. This is going to be a bug in the future for sure.

And believe me, this happens frequently and is one of the main causes of NullPointerException, although not the only one. So, take a lesson here always read the java docs of an API when you are using it for the first time (… at least ) [:-)].

A good solution to fix null problems is always initializing an object reference with some value, and never with null. In this way, we will never encounter NullPointerException. Fair enough. But in practice, we always don’t have a default value for a reference. So, how should those cases be handled?

3. How does an Optional fix the Problem?

Java Optional is a way of replacing a nullable T reference with a non-null value. An Optional may either contain a non-null T reference (in which case we say the value is “present”), or it may contain nothing (in which case we say the value is “absent”).

Remember that it is never said that an optional “contains null“.

Optional<Integer> optional = Optional.of(5);
optional.isPresent();          // returns true
optional.get();              // returns 5
 
Optional<Integer> optional1 = Optional.empty();
optional1.isPresent();          // returns false

We can also assume Optional as a single-value container that either contains a value or doesn’t.

It is important to note that the intention of the Optional class is not to replace every single null reference. Instead, its purpose is to help design more-comprehensible APIs so that by just reading the signature of a method, we can tell whether we can expect an optional value. This forces us to fetch the value from Optional and work on it, and at the same time handle the case where optional will be empty. Well, this is exactly the solution of null references/return values which ultimately result into NullPointerException.

4. Working with Optional

Below are a few examples of how Optional should be created and used in the application code.

4.1. Creating Optional

There are 3 commonly used ways to create an Optional.

UsingOptional.empty() to create empty optional.

Optional<Integer> possible = Optional.empty(); 

Using Optional.of() to create optional with default non-null value. If we pass null, a NullPointerException is thrown immediately.

Optional<Integer> possible = Optional.of(5);

Using Optional.ofNullable() to create an Optional that may hold a null value. If the argument is null, the resulting Optional object would be empty (remember that value is absent; don’t read it null).

Optional<Integer> possible = Optional.ofNullable(null); 
//or
Optional<Integer> possible = Optional.ofNullable(5);

4.2. Check if Value is Present in Optional

To check if Optional contains a non-null value, or Optional is not empty, use ifPresent() method.

Optional<Integer> optional = Optional.of(5); 

if(optional.isPresent()){
  Integer value = optional.get();
}

If the Optional object was empty, the execution will not enter in the if-block.

4.3. Default Values and Actions

A typical pattern in programming is to return a default value if we determine that the result of an operation is null. In general, we can use the ternary operator; but with Optional, we can write the code as below:

Optional<Company> companyOptional = Optional.empty();
 
//return a new instance if optional is empty
Company company = companyOptional.orElse(new Company());
 
//throw an exception if optional is empty
Company company = companyOptional.orElseThrow(IllegalStateException::new);

4.4. Rejecting Values using filter()

Often we need to call a method on an object and check some property. For example, in below code, we check if the company has a ‘Finance’ department; if it has, then print it.

Optional<Company> companyOptional = Optional.empty();

companyOptional.filter(department -> "Finance".equals(department.getName())
                    .ifPresent(() -> System.out.println("Finance is present"));

The filter() method takes a Predicate as the argument. If a value is present in the Optional object and matches the predicate, the filter() returns that value; otherwise, it returns an empty Optional object.

5. How does an Optional work?

If we open the source code of Optional.java, we will find that value that Optional holds is defined as:

/**
 * If non-null, the value; if null, indicates no value is present
 */
private final T value;

When we create an Optional then the below call happens at the end and assigns the passed value to ‘value‘ attribute.

this.value = Objects.requireNonNull(value);

When we try to get a value from an Optional, value is fetched if present otherwise NoSuchElementException is thrown:

public T get() {
	if (value == null) {
		throw new NoSuchElementException("No value present");
	}
	return value;
}

Similarly, other functions defined in Optional class operate around the ‘value’ attribute only. Browse the sourcecode of Optional.java for more insight.

The default no-args constructor is defined private, so we can’t create an instance of Optional except for the 3 given ways in section 2.

6. What is Optional Trying to Solve?

The Optional is an attempt to reduce the number of null pointer exceptions in Java systems, by adding the possibility to build more expressive APIs considering that sometimes return values are missing. If Optional existed since the beginning, today, 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.

By using Optional, the user is forced to think about the exceptional case. Besides the increase in readability that comes from giving null a name, the biggest advantage of Optional is its idiot-proof-ness. It forces us to actively think about the absent case if we want our program to compile since we have to unwrap the Optional and address the failure cases.

7. What is Optional not Trying to Solve?

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

Like when using null, Optional does not help with conveying the meaning of an absent value. So the caller of the method will still have to check the javadoc of the API to understand the meaning of the absent Optional, in order to deal with it properly.

Please note that Optional is not meant to be used in these below contexts, as possibly it won’t buy us anything:

  • in the domain model layer (it’s not serializable)
  • in DTOs (it’s not serializable)
  • in input parameters of methods
  • in constructor parameters

8. Best Practices

The Optional should be used almost always as the return type of a function that might not return a value.

This is a quote from the OpenJDK mailing list:

The JSR-335 EG felt fairly strongly that Optional should not be on any more than needed to support the optional-return idiom only. Someone suggested maybe even renaming it to “OptionalReturn“.

This essentially means that Optional should be used as the return type of certain service, repository or utility methods only where they truly serve the purpose.

9. Conclusion

In this article, we learned how we can adopt the new Java SE 8 java.util.Optional. The purpose of Optional is not to replace every single null reference in the code base but rather to help us design better APIs in which, just by reading the signature of a method, users can tell whether to expect an optional value and deal with it appropriately.

Happy Learning !!

Comments

Subscribe
Notify of
guest
3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode