Optional Class in Java 8

In this post on the Optional class in Java 8, we will delve into its essence, find its benefits, and see how it changes the way we manage null data. We’ll look at the mechanics of creating and utilizing Optional objects, as well as its many manipulation methods.

Optional Class in Java 8

What is Optional Class in Java 8?

The Optional class in Java 8 is a container type introduced to address the challenges and risks associated with handling null values.

Optional is a single-value container that either contains a value or doesn't (it is then said to be "empty").

Creating an Empty Optional

Optional emptyOptional = Optional.empty();

Creating an Optional with Value

Optional<Employee> employeeOptional = Optional.of(emp);

The above line may throw NullPointer Exception if emp is null.

We can use ofNullable method to avoid NullPointer Exception, in case emp is null, it will create an Empty Optional

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

isPresent method

isPresent() method is to find out whether a value is present in an Optional object.

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

if(employeeOptional.isPresent()){

System.out.println(employeeOptional.get().getName());

}

ifPresent method

ifPresent method takes a Consumer Type(Functional Interface) as an argument.A Consumer is implemented as a lambda expression:

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

employeeOptional.ifPresent(x -> System.out.println(x.getName()));

orElse method

orElse() method provides a default value if Optional is empty

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

Employee employee = employeeOptional.orElse(new Employee("Rochit"));

orElseGet method

orElseGet method takes a Supplier Type(Functional Interface) as an argument.A Supplier is implemented as a lambda expression:

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

Employee employee = employeeOptional.orElseGet(()->new Employee("Vivek"));

orElseThrow method

orElseThrow() method instead of providing a default value if Optional is empty, throws an exception

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

Employee employee = employeeOptional.orElseThrow(IllegalStateException::new);

filter method

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

Optional<Employee> employeeOptional = Optional.ofNullable(emp);

employeeOptional.filter(x -> "Gyan".equals(x.getName())).ifPresent(x -> System.out.println("Gyan is the Employee"));

Transforming Value with map() and flatMap

We can use map and flatMap to transform values.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper){...}

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper){...}

map and flatMap returns the result of the computation wrapped inside Optional. The difference is that map takes an unwrapped argument whereas flatMap takes a wrapped value(Optional value).

Employee empWithAddress = new Employee("Gyan", new Address("Street"));

Optional<Employee> empOptional = Optional.of(empWithAddress);

//Here getAddress() is an unwrapped value

String street= empOptional.map(x->x.getAddress()).get().getStreet();

//Here getAddressOptional() is an wrapped value(Optional Object)

String street1= empOptional.flatMap(x->x.getAddressOptional()).get().getStreet();

Java Code

import java.util.Optional;

public class OptionalExample {

 

public static void main(String[] args) {

// empty Optional

Optional<Employee> emptyOptional = Optional.empty();

Employee emp = new Employee("Gyan");

//Optional with a non-null value

//If emp were null, a NullPointerException would be immediately thrown

Optional<Employee> employeeOptional = Optional.of(emp);

//If emp were null, the resulting Optional object would be empty.

Optional<Employee> employeeOptionalSafe = Optional.ofNullable(emp);

 

//You can also use the isPresent() method to find out whether a value is present in an //Optional object. 

//In addition, there's a get() method that returns the value contained in the Optional //object, if it is present. 

//Otherwise, it throws a NoSuchElementException. The two methods can be combined, as //follows, to prevent exceptions:

 

if(employeeOptionalSafe.isPresent()){

 System.out.println(employeeOptional.get().getName());

}

 

//You no longer need to do an explicit null check; it is enforced by the type system. If the //Optional object were empty, nothing would be printed.

//Lambda expression for Consumer Type Functional Interface

employeeOptionalSafe.ifPresent(x -> System.out.println(x.getName()));

 

//Using an Optional object, you can rewrite this code by using the orElse() method, which //provides a default value if Optional is empty

 

 Employee employee = emptyOptional.orElse(new Employee("Rochit"));

System.out.println(employee.getName());

 

//Lambda expression for Supplier Type Functional Interface

Employee employee2 = emptyOptional.orElseGet(()->new Employee("Vivek"));

System.out.println(employee2.getName());

 

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

employeeOptional.filter(x -> "Gyan".equals(x.getName())).ifPresent(x -> System.out.println("Gyan is the Employee"));

 

 //you can use the orElseThrow() method, which instead of providing a default value if //Optional is empty, throws an exception

try {

emptyOptional.orElseThrow(IllegalStateException::new);

}catch(Exception ex) {

System.out.println("Exception:"+ex);

}

//Transforming Value with map()

//The map method returns the result of the computation wrapped inside Optional

//This makes it possible to apply and chain further operations on the response – such //orElse() here.

 

//Just like the map() method, we also have the flatMap() method as an alternative for //transforming values. 

//The difference is that map transforms unwrapped values whereas flatMap takes a //wrapped value and transforms it.

 

Employee empWithAddress = new Employee("Gyan", new Address("Street"));

Optional<Employee> empOptional = Optional.of(empWithAddress);

//Here getAddress() is an unwrapped value

String street= empOptional.map(x->x.getAddress()).get().getStreet();

//Here getAddressOptional() is an wrapped value(Optional Object)

String street1= empOptional.flatMap(x->x.getAddressOptional()).get().getStreet();

System.out.println("street using map:"+street);

System.out.println("street using flatmap:"+street1);

 

}

}
class Employee{
  
 private String name;
 private Address address;
 public Address getAddress() {
 return address;
 }
 public Optional<Address> getAddressOptional() {
 return Optional.ofNullable(address);
 }

public void setAddress(Address address) {

this.address = address;

}

public Employee(String name, Address address) {

this.name = name;

this.address = address;

}

public Employee(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

class Address{

private String street;

public Address(String street) {

this.street = street;

}

public String getStreet() {

return street;

}

public Optional<String> getStreetOptional() {

return Optional.ofNullable(street);

}

public void setStreet(String street) {

this.street = street;

}

}

Output

Gyan
Gyan
Rochit
Vivek
Gyan is the Employee
Exception:java.lang.IllegalStateException
street using map:Street
street using flatmap:Street

Best Practices: Optional Class in Java 8

Here are some best practices to consider when working with Optional Class in Java 8:

  1. Use Optional Selectively:
    • Use Optional for fields or return types that genuinely can be absent.
    • Avoid using Optional for non-nullable fields, as it adds unnecessary complexity.
  2. Prefer Factory Methods:
    • Use Optional.of() for values you know are non-null.
    • Use Optional.empty() when you explicitly want to represent absence.
    • Use Optional.ofNullable() when dealing with potentially null values.
  3. Avoid Nested Optional Instances:
    • Do not nest Optional instances within other Optional instances.
    • Return an empty Optional instead of Optional.empty() within an existing Optional.
  4. Avoid Overusing isPresent():
    • Instead of constantly checking with isPresent(), use orElse() or other methods to handle presence and absence gracefully.
  5. Use orElse() for Default Values:
    • Employ orElse(defaultValue) to provide a fallback value when a value is absent.
    • Use orElseGet(() -> defaultValue) when generating the fallback value is expensive.
  6. Use orElseThrow() for Failures:
    • Use orElseThrow() to throw an exception when a value is absent, indicating a failure case.
  7. Favor Functional Chaining:
    • Chain map(), flatMap(), and other methods to perform transformations and operations on optional values.
  8. Avoid Side Effects in orElse() or orElseGet():
    • Avoid performing side effects (like logging) within orElse() or orElseGet() to ensure consistency.
  9. Keep Streams in Mind:
    • If dealing with collections, consider using streams and their built-in null handling instead of Optional.
  10. Consider ifPresent():
    • Use ifPresent() when you want to perform an action only if a value is present.

Conclusion: Optional Class in Java 8

By using the Optional class in Java 8, programmers can:

  • Clearly communicate the potential absence of values in their code.
  • Avoid null pointer exceptions by explicitly dealing with absent values.
  • Streamline the process of providing default values when a value is absent.
  • Chain operations more effectively when working with optional values.
  • Foster a more readable and maintainable coding style.

Related Articles:

Top 40 Java 8 Interview Questions & Answers 2023

Java 8 features : Complete Guide

Leave a Reply

Your email address will not be published. Required fields are marked *