Your Guide to Java 8 Optional
It's never too late to dive into Java 8 goodies. Let's take a look!
Join the DZone community and get the full member experience.
Join For FreeOne of the most common exceptions Java programmers face is the NullPointerExceptions. This exception is thrown by the JVM at runtime as a runtime exception.
As we all know, a NullPointerException occurs when the application requires an object but it found a null value. Null value cases are one of the most common exceptions overlooked by Java programmers.
You may also like: 26 Reasons Why Using Optional Correctly Is Not Optional
Null values need to be handled in the application before proceeding with the usual business logic to avoid this exception at runtime. This leads to unnecessary null checks.
To deal with such type of boilerplate code for null values in Java, a new type Optional<T> was introduced in Java 8.
What Is the Problem Without Java 8 Optional?
According to Oracle, Java 8 Optional works as a container type for the value which is probably absent or null. Java Optional is a final class present in the java.util package.
Let’s check out the problem without Java 8 Optional.
Suppose we have the following method in our application. This method retrieves the employee details from the database and returns it for the respective id :
x
Employee findEmployee(String id) {
...
};
Suppose the provided id is not present in the database. Then, the method will return the null value. Now, if we have the code written below:
x
Employee employee = findEmployee("1234");
System.out.println("Employee's Name = " + employee.getName());
The code above will throw the NullPointerException at runtime as the programmer has not null checked the value before using it.
How Do Java 8 Optionals Provide the Solution?
Now, let us see how Java 8 Optional will solve the above problem and help in eliminating the NullPointerException
.
Below is the modification required for the above code :
x
Optional < Employee > findEmployee(String id) {
...
};
In the above code, we are indicating to the client by returning Optional<Employee> that there is a possibility that an employee may not exist with the given id.
Now, in the client’s application, this fact needs to be explicitly communicated.
The client should be written as follows:
x
Optional < Employee > optional = findEmployee("1234");
optional.ifPresent(employee -> {
System.out.println("Employee name is " + employee.getName());
})
You can see that we have created one Optional object in the above code’s first line. Now, we are allowed to use various utility methods with the Optional object.
The method ifPresent()
in the above code snippet calls the provided lambda expression only if the employee is present; otherwise, it does not.
Advantages of Java 8 Optional
Below are some of the listed advantages of using Java 8 Optional:
- NullPointerException is prevented at runtime.
- Null value checking is not required in the application.
- Boilerplate code is not required.
- It's easy to develop clean and neat APIs.
Java 8 Optional Class Methods
Methods | Description |
---|---|
public static <T> Optional<T> empty() | This method returns an empty Optional object. No value is present for this Optional. |
public static <T> Optional<T> of(T value) | This method returns an Optional with the specified value that is not null. |
public static <T> Optional<T> ofNullable(T value) | This method returns an Optional describing the specified value if the value is non-null; otherwise, it returns an empty Optional. |
public T get() | If a value is present in this Optional, then it returns the value. Otherwise, it throws NoSuchElementException. |
public boolean isPresent() | This method returns a true value if there is a value present. Otherwise, it returns false. |
public void ifPresent(Consumer<? super T> consumer) | If a value is present, then the consumer with the provided value is invoked. Otherwise, it does nothing. |
public Optional<T> filter(Predicate<? super T> predicate) | If a value is present and it also matches the given predicate, then it returns an Optional describing the value. Otherwise, it returns an empty Optional. |
public <U> Optional<U> map(Function<? super T,? extends U> mapper) | If a value is present, then the mapping function is applied, and if the result is not a null value, then it returns an Optional describing the result. Otherwise, it returns an empty Optional. |
public <U> Optional<U> flatMap(Function<? super T,Optional<U> mapper) | If the value is present, then it applies the provided Optional-bearing mapping function to it and it returns that result. Otherwise, it returns an empty Optional. |
public T orElse(T other) | This method returns the value if present; otherwise, it returns other. |
public T orElseGet(Supplier<? extends T> other) | This method returns the value if present. Otherwise, it invokes other and returns the result of the invocation. |
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X extends Throwable | If the value is present, this method returns the contained value. Otherwise, it throws an exception to be created by the provided supplier. |
public boolean equals(Object obj) | This method is used for indicating whether some other object is “equal to” this Optional or not. |
public int hashCode() | This method returns the hash code value of the present value if it exists. Otherwise, it returns 0 (zero) . |
public String toString() | This method is simply used to return a non-empty string representation of the Optional, which is suitable for debugging. |
Creating a Java 8 Optional Object
In this section, we are going to look at the different ways we can create the Java 8 Optional object:
1. Create an empty Optional object
The code below shows how to create an Optional object that has a null value. It simply describes the absence of a value.
x
Optional < Employee > employee = Optional.empty();
2. Create an Optional object with a non-null value
The code below shows how to create an Optional object that has a non-null value.
x
Employee employee = new Employee("1234", "TechBlogStation");
Optional < Employee > optional = Optional.of(employee);
It is important to note that if null is supplied to the argument of Optional.of()
, then it will throw the NullPointerException immediately and will not allow you to create the Optional object.
3. Create an Optional object with a value that can be both null and non-null
The code below shows how to create an Optional object that is allowed to have both null and non-null values.
x
Optional < Employee > optional = Optional.ofNullable(employee);
If a non-null value is passed in Optional.ofNullable()
, then it will return the Optional containing the specified value. Otherwise, it just returns an empty Optional.
Checking the Presence of a Value in Java 8 Optional Object
Now, let's learn the different ways to check the presence of a value in the Java 8 Optional object through different methods:
1. isPresent()
method
The method isPresent()
returns the value true in case the id of the Optional objects contains a non-null value. Otherwise, it returns a false value.
x
if (optional.isPresent()) { // Suppose the non null value is present in Optional System.out.println("Value - " + optional.get()); } else { // Suppose the null value is present in Optional System.out.println("Optional is empty"); }
2. ifPresent()
method
In the method ifPresent()
, we pass the Consumer function. That Consumer function will only be executed if a value is present in the Optional object.
If Optional is empty, then it does nothing:
x
optional.ifPresent(value -> {
System.out.println("Value present - " + value);
});
In the code above, we have supplied the lambda function as the parameter of the ifPresent()
method.
Retrieving the Value From Java 8 Optional Object Using get() Method
The get()
method of Optional is simply used to return a value from the Optional object. Suppose the value is not present, then it throws the exception NoSuchElementException.
xxxxxxxxxx
Employee employee = optional.get()
In the case of the absence of a value, it throws an exception, so it is recommended that before using the get()
method, we should first check if the value is present or not.
Returning the Default Value From Java 8 Optional Object Using orElse() Method
The method orElse()
is used to return a default value if the Optional object is empty.
See the example below:
xxxxxxxxxx
// Below will return "Unknown Employee" if employee is null User finalEmployee = (employee != null) ? employee : new Employee("0", "Unknown Employee");
Now, write the above logic using Java 8 Optional’s orElse()
method:
xxxxxxxxxx
// Below will return "Unknown Employee" if employee is null User finalEmployee = optional.orElse(new Employee("0", "Unknown Employee"));
Returning the Default Value From Java 8 Optional Object Using orElseGet() Method
As we learned, the method orElse()
returns one default value directly in case the Optional object is empty, but theorElseGet()
method accepts a Supplier and that Supplier is invoked when Optional is empty.
The result that is returned by the Supplier becomes the default value for Optional.
x
User finalEmployee = optional.orElseGet(() -> {
return new Employee("0", "Unknown Employee");
});
Throw an Exception if the Value Is Not Present in Java 8 Optional
The orElseThrow()
method is used for throwing an exception if the Optional object is empty.
It can be used in the scenario where the object for the specified request parameter does not exist in the REST API. You can use this method to throw custom exceptions like ResourceNotFound()
, etc.:
x
"/employees/{id}") (
public User getEmployee( ("id") String id) {
return employeeRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Employee not found with id " + id););
}
Filter the Values Using Filter() Method of Optional
Suppose you have an Optional object of an Employee. Now, you are checking for the gender of the employee and calling a function correspondingly.
Below is the old approach for doing so:
x
if(employee != null && employee.getGender().equalsIgnoreCase("MALE")) { // calling the function }
Now, let us see how can we use Optional filter to achieve this:
x
optional.filter(user -> employee.getGender().equalsIgnoreCase("MALE")) .ifPresent(() -> { // Your function })
The filter()
method takes a predicate as a parameter. In case the Optional is containing a non-null value and the value matches the provided predicate, then this method returns an Optional containing that value.
Otherwise, this method returns an empty Optional.
Extract and Transform Values Using map()
Suppose we have a scenario where we want to extract the address of an employee and also want to print its location depending upon a specified condition.
Consider the below example:
We have the getAddress()
method in our Employee
class :
x
Address getAddress() {
return this.address;
}
Below is the typical approach to achieve the requested scenario:
x
if (employee != null) {
Address address = employee.getAddress();
if (address != null && address.getCountry().equalsIgnoreCase("USA")) {
System.out.println("Employee belongs to USA");
}
}
Now see how we can use the map()
method of Optional to achieve the same results:
x
userOptional.map(Employee::getAddress).filter(address -> address.getCountry().equalsIgnoreCase("USA")).ifPresent(() -> {
System.out.println("Employee belongs to USA");
});
The above code, in comparison to the previous approach, is well readable, concise, and efficient.
Let’s understand the code in greater detail:
xxxxxxxxxx
// Extracting the Employee address using map() method. Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
// filtering the address from USA Optional<Address> usaAddressOptional = addressOptional.filter(address -> address.getCountry().equalsIgnoreCase("USA"));
// Printing if country is USA usaAddressOptional.ifPresent(() -> { System.out.println("Employee belongs to USA"); });
In the above code snippets, the method map()
is returning an empty Optional in one of the following cases:
- If the employee is not present in
employeeOptional
. - If the employee is present but the method
getAddress()
returns null.
Otherwise, it simply returns an Optional<Address> that contains the employee address.
Cascading Optionals Using flatMap()
Now, let us again consider the above example related to the map() method.
You can see even if the Employee address can be null, then why are we not returning the Optional<Address> instead of a simple Address extracted using the getAddress()
method.
It will be incorrect as we will have a problem in the following line of code if we return the Optional<Address>:
x
Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
Now, the method getAddress()
is returning Optional<Address>
, so the return type of employeeOptional.map()
will be Optional<Optional<Address>>
.
This is further demonstrated below:
xxxxxxxxxx
Optional<Optional<Address>> addressOptional = employeeOptional.map(Employee::getAddress)
Now, we have nested Optional and we don’t want that, so we can now use the flatMap()
method to solve this problem:
xxxxxxxxxx
Optional<Address> addressOptional = employeeOptional.flatMap(Employee::getAddress)
Please note that If your mapping function is returning an Optional, then use
flatMap()
instead ofmap()
to get the flattened result from your Optional object.
Conclusion
We have learned what Java 8 Optional is, its advantages, and problems solved by using Optional in Java. Additionally, we were able to better understand different methods of Java 8 Optional by looking at some illustrative examples.
Thanks for reading!
Further Reading
Java 8 Optional Uses and Best Practices
Published at DZone with permission of Shatakshi Dixit. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments