How to use Java Optional

Advertisements

The Optional class was introduced with Java 8, and even though for many developers it’s just a way to handle differently the null check for variables/instances its real purpose is to introduce functional style coding in Java. The idea behind this article is to provide some best practices using the Optional giving a functional code style and simplifying our code.

Creating an Optional

We have 2 methods to create Optional instances:

  • Optional.of(value)
  • Optional.ofNullable(value)
Optional<String> myString1 = Optional.of("Hello world!"); //populated optional
Optional<String> myString2 = Optional.of(null); //Throws an NullPointer Exception
Optional<String> myString3 = Optional.ofNullable("Hello world!"); //populated optional
Optional<String> myString4 = Optional.ofNullable(null); //empty optional

Let’s use Optional.ofNullable when possible then we will handle correctly any null value. As we can see in the above code, using Optional.of(null) will throw a NullPointerException, therefore is better only to use it when you are 100% the value will not be null.

isPresent() and isEmpty()

We can use the instance method of an Optional isPresent and isEmpty to know whether the instance has a value or not. That’s exactly the code that we want to avoid!

  Optional<String> myOptional = doSomething();
  String myValue;
  if (myOptional.isPresent()) {
    myValue = myOptional.get();
  } else {
    myValue = null;
  }

There is no bigger difference between using the old null (var != null) check to the isPresent or isEmpty method. We are not taking advantage of the benefits of the Optional, and in fact, our code looks more verbose. If we really want to take advantage of its benefits, let’s switch the mindset and let’s have closer functional programming thinking.

Optional and functional style code

We will see how most of our code using Optional can be enhanced using methods like map, filter, orElse, orElseThrow, ifPresentOrElse, reducing the use of the isEmpty and isPresent methods at the minimum, at the same time, doing your code more readable.

For example, instead of using the if(optional.isPresent) statement, we can replace it with the ifPresentOrElse or the only the ifPresent, to execute some logic.

  String myValue = null;
  Optional<String> myOptional = doSomething();
  myOptional.ifPresent(foo -> { doMoreThings(foo);/** more code here **/});
  Car myCar;
  Optional<Car> myOptionalCar = doSomethingCar();
 
  myOptional.ifPresentOrElse( 
    foo -> { /** more code here **/ },
    () -> { /** more code here **/ }
  );

Some times, we don’t have to execute some logic, but maybe to get the value of the Optional and store it in a variable. For those use cases, we can use the orElse method

Optional<Foo> possibleFoo = doSomething();
Foo foo = possibleFoo.orElse(new Foo());

or doing it lazily with the orElseGet(…) method.

Optional<Foo> possibleFoo = doSomething();
Foo foo = possibleFoo.orElseGet(() -> {/**Some logic that returns a Foo**/});

And what about throwing an Exception when the Optional is empty, for example, finding a record in the database. For that purpose, we have the orElseThrow(()-> {...}) method.

Optional<User> maybeUser = findUserById(userId);
User user = maybeUser.orElseThrow(() -> new NotFoundException("User not found"));
//If user is present we continue here.

Some other important methods from the Optional class that we can use map and filter. We can use the map to transform it into another object, one advantage is that if the Optional is empty then it map logic will not be called.

Optional<User> maybeUser = findUserById(userId);
String userName = maybeUser.map(u -> user.getName()+ " " + user.getLastname())
  .orElseThrow(() -> new NotFoundException("User not found"));

We use the filter method to reduce the result if the Optional is not empty. If the filter criteria is false then it will return an empty Optional.

Optional<User> maybeUser = findUserById(userId);
User adultUser = maybeUser.filter(u -> u.getAge() > 18 )
  .orElseThrow(() -> new NotFoundException("User is not an adult"));

And finally, we can notice how we can use more than one of the Optional methods in the same line, for example: map -> filter -> map-> getOrElse. So our code will require less code lines.

Conclusion, we didn’t need to use isPresent() or isEmpty() method, at the same time our code is less verbose, we applied a functional style code. That’s the real advantage of the Optional class

When and when not to use Optional

As we have seen Optional is excellent to be used as a return type (this is its purpose), but what avoid fields or method parameters. I recommend following these best practices.

– Fields

In summary, avoid using instance fields as Optional, why?

  • Because Optional class are not Serializable.
  • Because fields constitute part of the state of the object. As an alternative, you can create a getter accessor that returns an Optional.
public class MyObject {
   private String myField;
  
   public Optional<String> getMyField(){
     return Optional.ofNullable(myField); 
   }
}

– Parameters

There is no restriction to use an Optional as a parameter, but you mind want to consider that Optional is still an Object and someone can send null as a parameter of the method.

References

Advertisements

Leave a Reply

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