Java - Null Handling With Optionals

Ömer Kurular
Javarevisited
Published in
6 min readMay 24, 2021

In this article, we are going to cover one of the essential topics of java, null handling, and we are going to do this with Optionals that was introduced in Java 8 and we will be doing that in full detail with examples.

We are going to create Optional returning methods and also see the Spring way of using Optionals for database operations. Firstly, let’s remember what NullPointerException is and then look at what Optionals are.

In Java, if we want to create a variable, we first declare it and then initialize it. When we use a reference that points nowhere in the memory (uninitialized) and operate over it (e.g. calling methods), we get NullPointerException. You can see an example below.

Object someObject = null; // created null variable
someObject.toString(); // method call over null variable

Here, we have uninitialized object called someObject and tried to call toString method over it. As it is not initialized and refers to nowhere in the memory, it will end up throwing NullPointerException. Now, if we refreshed our memory, we can get to Optionals.

1- What is Optional?

Optional class provides a way to handle nullable objects in a elegant way with fluent API. With the provided API, we will process over the object if the object is present (not null). Let’s see what it looks like

Object someObject = null;
Optional<Object> objectOptional = Optional.ofNullable(someObject);
System.out.println(objectOptional.isPresent()); // prints false

As you see above, we set the someObject variable to null and then created the Optional object wrapping the null object. Then, we checked if the variable is null or not with the method isPresent. And let’s try with non-null object and see it is printing true as we pass non-null object to Optional wrapper.

Object someObject2 = new Object();
Optional<Object> objectOptional2 = Optional.ofNullable(someObject2);
System.out.println(objectOptional2.isPresent()); // prints true

2- Methods of Optional Class

Now, let’s see what methods we can use to get the most out of the Optional class.

We can start with creational methods. These creational methods are static methods in the Optional class.

  • ofNullable(T value), creates Optional wrapping an object which can be null or non-null.
Optional<Object> optional = Optional.ofNullable(new Object());
  • of(T value), creates Optional wrapping an object which cannot be null.
Optional<Object> optional = Optional.of(new Object());

You will probably ask “why would I use it if we make sure the passed object is not null”. There may be some cases you need to return an Optional object in your method as a result of the implementation of a method of an interface, so you may need this method.

  • empty(), creates empty Optional which means no value is present inside Optional object.
Optional<Object> optional = Optional.empty();

Now, let’s look at methods that check presence of value.

  • isPresent(), returns true if the Optional contains a non-null value and false otherwise.
Optional<Object> optional = Optional.ofNullable(new Object());
System.out.println(optional.isPresent()); // prints true
  • ifPresent(Consumer<? super T> consumer), lets you run your own logic in case the value inside Optional is present.
Optional<String> stringOptional = Optional.of("Hi!");
Optional<String> stringOptionalNull = Optional.empty();

stringOptional.ifPresent(System.out::println); // prints "Hi!"
stringOptionalNull.ifPresent(System.out::println); // prints nothing

You can either use isPresent with the traditional if statement or you can prefer ifPresent for an elegant way of handling existing value.

We sometimes need to run some logic if the value is not present such as providing backup value. Let’s see how we can handle non-existing value.

  • orElse(T other), returns the value inside Optional if present, otherwise returns the given value as parameter.
Optional<String> stringOptionalNull = Optional.empty();
String backupValue = stringOptionalNull.orElse("Hi!");
System.out.println(backupValue); // prints "Hi!"

As you see, stringOptionalNull is an empty Optional and we provide a backup value in case it is not present thanks to orElse method. So, it prints the value given to orElse method.

  • orElseGet(Supplier<? extends T> other), returns the value inside Optional if present, otherwise runs the supplied logic and then returns its result.
Optional<String> stringOptionalNull = Optional.empty();
String backupValue = stringOptionalNull.orElseGet(() -> "Hi!");
System.out.println(backupValue); // prints "Hi!"

If we provide a backup value after a series of operations, we can take advantage of orElseGet method.

Important note below

Before talking about another method, we will see an important difference between orElse and orElseGet method. When you provide a function to the orElse method, it will always run the given method no matter if the value is present inside Optional. For orElseGet method, it will not run the given method if the value is present inside Optional. Let’s see this with an example.

public static void main(String[] args) {
Optional<String> stringOptional = Optional.of("I exist..");
String value = stringOptional.orElse(getStr());
String value2 = stringOptional.orElseGet(() -> getStr());
}public static String getStr() {
System.out.println("I am being evaluated");
return "Hi!";
}
// Above program prints only one "I am being evaluated"

We first created an optional string with the value “I exist..”. Then, we apply orElse method by giving getStr() method as parameter. As we said before, eventhough value is present, orElse will evaluate the method given to it. So, it will print “I am being evaluated”. Then, we have orElseGet method with the given lambda expression. This time, it is not going to run given lambda as the value is present. If the value was not present, e.g. initially Optional.empty(), it would print two “I am being evaluated”. Let’s continue with the last function we will cover.

  • orElseThrow(Supplier<? extends X> exceptionSupplier), throws the provided Exception in case the value is not present.
Optional<String> stringOptionalNull = Optional.empty();
String backupValue = stringOptional.orElseThrow(RuntimeException::new);
// Result
// Exception in thread "main" java.lang.RuntimeException
// at java.util.Optional.orElseThrow(Optional.java:290)
// at optional.Demo.main(Demo.java:29)

We can see that orElseThrow throws exception as the value inside Optional is not present.

  • map(Function<? super T, ? extends U> mapper), maps the value inside optional via given map function.
Optional<String> stringOptional = Optional.of("Hi, there!");
int length = stringOptional.map(String::length).orElse(0);

Above snippet shows the mapping of initially String optional to Integer.

  • filter(Predicate<? super T> predicate), filters the value inside Optional object with given Predicate.
Optional<String> stringOptional = Optional.of("I love java");
stringOptional.filter(str -> str.endsWith("java")).ifPresent(System.out::println);

The code block above will run the code block inside ifPresent as the filter we applied to Optional object evaluates true.

Finally, let’s look at the most basic method we can use to get the value inside Optional object.

  • get(), simply returns the value inside the Optional object.

We need to use get function carefully with presence check first. If no value exists inside Optional object, then this method will throw NoSuchElementException. So, we can use it as follows or just use ifPresent method.

if (someConditional.isPresent()) {
Object value = someConditional.get();
}

Examples

As we covered Optional methods, we can have a look at some examples in core Java and Spring applications.

Imagine we have a class that works as cache and we can get objects with keys of type String. In this case, would not it make perfect sense to use Optionals as we may not have an object associated with a given key? Let’s see it in action

public enum  ObjectCache {
INSTANCE;

private final Map<String, Object> objectMap = new HashMap<>();

public Optional<Object> get(String key) {
return Optional.ofNullable(objectMap.get(key));
}

public void add(String key, Object value) {
objectMap.put(key, value);
}

public Optional<Object> delete(String key) {
return Optional.ofNullable(objectMap.remove(key));
}
}

In the ObjectCache class, we have three methods that interact with the objectMap. When we use get method with a key, it may not always have associated object and if we operate over the returned null object, we face NullPointerException. But in the above class, we return Optional and the client can use it with the methods we talked about before to ensure null safety.

Another use case can be in Spring applications. In the data layer, we do some queries and they return values from the database. With the same logic as above, it may not have a corresponding value so it may return null value. In this case, it is better to return values wrapped in Optionals. Let’s see it.

@Repository
public interface UserRepository extends JpaRepository<User, String> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
}

Here, we have two methods. For example, for the findByUsername method, we pass username and it returns related user from database. But, such user may not exist. In this case, we better return User wrapped inside Optional to handle nulls better.

In this article, I talked about null handling in Java with Optionals. I hope you like it. For more article like this, you can follow me.

https://github.com/kurular4/medium-java/tree/master/optionals

--

--

Ömer Kurular
Javarevisited

I am Ömer and currently working as a full-time software engineer. I will share my knowledge with you I gained through years of professional and self working.