avatarViraj Shetty

Summary

The article provides guidelines for effectively using Java's Optional class to handle potential null values, emphasizing its purpose, creation, and consumption.

Abstract

Java's Optional class, introduced in JDK 8, serves as a container for objects that may be null, aiming to prevent NullPointerException instances. The article outlines four key rules for using Optional: prefer Optional.ofNullable when the possibility of null exists, always check for the presence of a value before calling Optional.get, avoid returning null as an Optional by using Optional.empty instead, and limit the use of Optional to return types, not as method parameters or class member variables. The article also touches on functional programming styles with Optional and encourages its judicious use, particularly in library code, to improve code robustness and clarity.

Opinions

  • The author suggests that the Optional class forces developers to actively consider null values, similar to handling checked exceptions.
  • It is recommended that Optional be primarily used in library code where its purpose is to convey potential null values clearly.
  • The author advises against overusing Optional, particularly as a method parameter or a member variable, to avoid code that is difficult to read and maintain.
  • The article promotes the use of Optional's functional-style methods, such as ifPresent, orElse, orElseGet, filter, map, and flatMap, to write concise and readable code.
  • The author emphasizes that returning null as an Optional object defeats the purpose of using Optional and should be avoided.
  • The article encourages developers to adopt Optional to make their applications more robust and less prone to null-related errors.

4 Rules for using Java Optional

This article will explore why and how to use Java’s Optional class. We will also provide 4 simple rules for using it effectively.

Photo by Christopher Gower on Unsplash

Why Optional?

Let’s take a look at the code below to see why Java’s Optional class was introduced in JDK 8.

public class Users {

  public static User getUser(String uid) {

      User user = null;

      // logic to set user

      return user;
  }
}


// Calling the getUser method. 
// Will get NullPointerException
User user = Users.getUser(uid);
String name = user.name();

In the code, you see that there is a Users class (used as a User Factory class) which has a method to return a User object and based on some logic (maybe fetching from the Database) returns the data back. The problem here is that, it’s perfectly possible for the user object to be null.

A standard way to use such a method is to call it and check if the user is null. If not null, then proceed to handle the user. If the null check was not in place (as in the code above), your application would throw a Null Pointer Exception. So, here’s a question ?

How can the programmer who coded the getUser(..) method convey to the caller that “null” is a valid return value and that the caller must handle it ?

That’s what the Optional class is all about. Optional types make sure that the caller of an API (method) is forced to at least “think” about nulls. It’s entirely possible that the caller might ignore this recommendation by the API programmer.

What is an Optional ?

Optional type is a container for the actual object that needs to be returned. The name of the class itself is Optional and it can be used as a parameterized type — which means it can contain any type of object.

The big idea here is that if a method returns a value which “can” be null, then that method can instead return an Optional with the value embedded inside it. Such a method will not return a null.

When we think about Optional types, it’s useful to think from two perspectives — creating an Optional object and consuming an Optional object. The API or method writer will be more interested in creating the Optional and returning it back; whereas the API caller will be more interested in how to consume the Optional type.

So, let’s start with some methods which are useful for creating an object of type Optional.

Creating an Optional

Here’s the simplest possible way to create an Optional object using a static method called of(..)

// User Record 
record User(String uid, String name, 
            boolean ceo) {
}

// Create an Optional from a non-null object
User user = new User("1","John Doe",false);
Optional<User> optUser = Optional.of(user);

Here we create an Optional from a non-null object. The way to do this is to use the Optional.of(..) method. We should be careful here because the value passed must not be null. Otherwise we will get a NullPointerException — the very outcome we want to avoid.

But what if we explicitly want to return null ? Remember when we use Java Optional, we don’t ever return a null but we return an empty Optional — an Optional with an empty value.

// Create an empty Optional object
Optional<User> optUser = Optional.empty();

The example code above demonstrates how to create an empty Optional by simply calling the static Optional.empty() method.

What if we do not know whether the value is null or not ? Maybe we retrieved it by calling another method and we cannot be sure. That’s the third way as demonstrated below.

// Create an Optional from an object which 
// "might" be null
User someUser = ....
Optional<User> optUser 
       = Optional.ofNullable(someUser);

In the example above, someUser variable may or may not be null. So, we use Optional.ofNullable(..) to create the Optional object. It’s safer to use this method rather than the Optional.of(..) method so that null is appropriately taken care of.

Rule 1 — When in doubt use Optional.ofNullable(..) instead of Optional.of(..)

Now let’s take a look at ways to consume the Optional. When the caller calls the Users.getUser(..) method, how does the caller handle the returned Optional object?

Consuming an Optional

If you want to check if a value is present in the Optional object, then use the method isPresent().

// Check if optional has a value
Optional<User> optUser = Users.getUser(uid);
boolean present = optUser.isPresent();
if (present) {
   User user = optUser.get();
   …
}

In the example code above, the caller uses isPresent() method to check if the Optional has a valid value. If true, the caller proceeds to get the actual User object.

What if we want to check if the Optional object is empty?

// Check if optional is empty
boolean empty = optUser.isEmpty();

Simply use the isEmpty() method on the Optional object to check if the Optional is empty.

With this knowledge under our belt, let’s now rewrite the Users.getUser(..) method to get a User using Java Optional.

Using Optional in Code

Below is the refactored code for Users class and the calling class to use Java Optional.

public class Users {

  public static Optional<User> getUser(String uid) {

      User user = null;

      // logic to set user

      return Optional.ofNullable(user);
  }
}

// Calling the getUser method which returns Optional
Optional<User> optUser = Users.getUser(uid);
if (optUser.isPresent()) {

   // process User. It's not empty
   String name = optUser.get().name();
}

You can see that we use the method ofNullable(..) to return the Optional with the embedded User object. The caller, on the other hand, uses the isPresent() method to determine if the Optional has a valid user.

So how exactly does this improve the code ? The caller would be forced to use the isPresent() to check if the returned Optional object has a value and therefore is forced to “think” about the null value. In spirit, the idea is similar to throwing a checked Exception which also forces the caller to handle the Exception.

Having said that, the caller can completely disregard this and do the following.

// BAD BAD coding. May get NullPointerException
Optional<User> optUser = Users.getUser(uid);
String name = optUser.get().name();

Note here that we are calling the get() method on the Optional object without checking if the Optional has a value or not. This is poor programming and if you plan to do this for whatever reason, it’s worthwhile documenting why you think the Optional will always have a value.

Rule 2 — Don’t call Optional.get() without checking if value is present

Let’s take a look at another problem.

public class Users {

   public static Optional<User> getUser(String uid) {

       User user = null;

       // Maybe there is a bug which returns a null
       // under some conditions
       return null; 
   }
}

Here you see that the getUser(..) method is actually returning an Optional object which is null. This could be a bug in the system but it’s a disaster written all over. The whole point of Optional use is to protect the calling code from null. By returning an Optional Object which itself is null, we have created a rather funny and ironic situation.

Rule 3 — Do not ever return a null as an Optional but instead return an empty Optional

Using Java Optional Judiciously

A note of caution here — the recommendation by Java Engineers is to use Java Optional when writing libraries which are consumed by many other developers and there is a need for clarity of the methods. It’s definitely not the intention to design every method call in every class to return an Optional where null can be returned.

Furthermore, Java’s Optional class was designed to be used as a return type for methods which could return a null value. Even though we can use Optional type as a parameter to a method — this should be avoided. This leads to over use of Optional and leads to unreadable code. Similarly, avoid using Optional as member variables of a class because Optional is not Serializable.

Using a Java Optional for just return type from methods is a good balance and that too for parts of code which are used often (maybe libraries). As Java developers, we will probably find ourselves consuming Optional objects a lot more than creating them.

Rule 4 — Use Optional type only as a Return type and not as a method parameter or a member variable of a class

With this, we now know how to use Java’s Optional class; we know what it is, how to create them and how to consume them.

Using Optional in Functional Style

Optional class also has a number of functional and fluent style methods that can be useful if you are a big fan of functional programming. Examples of these methods are ifPresent(..), orElse(..), orElseGet(..), filter(..), map(..), flatMap(..) and others. Using the functional style of programming — programmers can create short, fluent and readable code.

If you want to learn more about the fluent style of development using Optional, you can read my Medium article below.

If this post was helpful, please click the clap 👏 button below a few times to show your support. Thanks for reading !

A YouTube Video version of the article can be found at https://youtu.be/M78ucGVBXIU

Also Check out my discounted Udemy Courses at https://www.mudraservices.com/courses/

Summary

As library programmers or when writing reusable software, we should consider using Java Optional class as a return type to express null behavior. By using a few simple rules for Optional, your entire application will be more robust and bullet-proof.

Java
Java Optional
Nullpointer
Recommended from ReadMedium