DEV Community

Cover image for Optional: Handling NullPointer Graciously
yash sugandh
yash sugandh

Posted on

Optional: Handling NullPointer Graciously

We all know about a billion dollar mistake known as the NullPointer Reference, we all have regular encounters with Null Pointer and sometimes even had to face the wrath of NullPointer Exception. The topic we are going to look into today Optional is going to provide us a way to handle this issue much more graciously.

What is Optional and why was it introduced?

Optional is class introduced in Java 8 under java.util package that allows us to avoid NullPointer Exception that we encounter a lot of times.

Optional provides us a way to check and use whether a value is null or not .This way we can save ourself from the wrath of NullPointers and handle them in a way that is elegant, easy to use and maintain i.e. without using a ton of if-else everywhere to check if an object is null or not.

Where can we find Optional being used?

Now we all know that we should never return null from a method, right?

If you are one of those people who return null from a method and you know who you are don't hide it, please refrain from doing so.

Jokes aside, there are other scenarios where we are not sure if we will receive a value or not, such as when we use Streams findfirst() method i.e. a method that returns the first value of a Stream if the Stream is non-empty so there is possibility a value may not be returned in case the Stream is empty hence an Optional is returned.

Another example that we generally use is the findById() method which is used to get the data from database on the bases of an id so the method may not return a value in case Id is not found in database hence provides us with an optional.

How do we create an Optional?

There are three different ways of creating an Optional

1.Creating an empty Optional

Optional.empty()

The above method creates an empty Optional i.e. an optional with no value inside

2.Creating an Optional some non null value
Optional.of(10)
Optional.of("abcd")
and so on

We can create Optional with different type of values integer, string or an object.

For the cases where values can also be null we have the following

3.Creating an Optional which can also contain null value

Optioanl.ofNullable(10)
Optional.ofNullable(null)
and so on.

The Optional.ofNullable() can be used in the cases where there is a possibility of the value being a null reference.

Now that we know how to create Optionals the next step is to check whether an Optional contains a value or not

1.isPresent()

In Java 8 the best way to check whether an Optional has a value or not is to use the method isPresent().

This method structure is as follows

   public boolean isPresent() {
        return value != null;
    }
Enter fullscreen mode Exit fullscreen mode

The method return us a boolean after checking if a value is present inside an Optional or not.

It returns a true if a value is present otherwise it returns false.

Let's look at example on how to use the method

   final Optional<Integer> optionalInteger = Optional.of(10);
   final Optional<Object> emptyOptional = Optional.empty();

   System.out.println(emptyOptional.isPresent());
   System.out.println(optionalInteger.isPresent());
Enter fullscreen mode Exit fullscreen mode

The output for emptyOptional is be false and the optionalInteger is true.

2.isEmpty()

In Java 11 another method was introduced for checking whether optional contains a value or not.

This method structure is as follows

    public boolean isEmpty() {
        return value == null;
    }
Enter fullscreen mode Exit fullscreen mode

If a value is not present it returns true, otherwise it returns false.

Let's look at example on how to use the isEmpty() method

 final Optional<Integer> optionalInteger = Optional.of(10);
 final Optional<Object> emptyOptional = Optional.empty();
 System.out.println(emptyOptional.isEmpty());
 System.out.println(optionalInteger.isEmpty());
Enter fullscreen mode Exit fullscreen mode

The output for emptyOptional is be true and the optionalInteger is false.

From what we can see from examples both the isPresent() and isEmpty() work in kind of similar manner except that one checks whether the value is present in Optional and other checks for whether the Optional is empty or not.

So based on the requirement we have we can use either or those.

There are cases where we want to perform an operation if the value is present.

In this scenario we can of course use the combination of if(isPresent()) but this will just lead us back to how we use to deal with null references before Optional using if-else statement which we don't want to do.

So for use cases like this we have a method

3.ifPresent()

The ifPresent() method will only execute the operation if the Optional contains some non null value.

This method structure is as follows

    public void ifPresent(Consumer<? super T> action) {
        if (value != null) {
            action.accept(value);
        }
    }
Enter fullscreen mode Exit fullscreen mode

The method does not return anything and what it takes as a parameter is a Consumer.

Let's take an example on how to use ifPresent()

 final Optional<Integer> optionalInteger = Optional.of(10);

 optionalInteger.ifPresent(integer -> System.out.println(integer + 10));
Enter fullscreen mode Exit fullscreen mode

In the example above we can see that the output will only be printed if the value is present and in other cases no output will be printed

What if we want to print something or execute a different function if Optional does not contain any value?

4.ifPresentOrElse()

Introduced in Java 9, the ifPresentOrElse() allows us to provide an alternative in which even if the Optional is empty we can perform an action

This method structure is as follows

    public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (value != null) {
            action.accept(value);
        } else {
            emptyAction.run();
        }
    }
Enter fullscreen mode Exit fullscreen mode

So if a value is present, it will perform the given action with the value, otherwise it will perform the given empty-based action.

Let's take an example on how to use ifPresentOrElse()

    final Optional<Object> emptyOptional = Optional.empty();

    emptyOptional.ifPresentOrElse(
        val -> System.out.println("The value present inside the optional is  " + val),
        () -> System.out.println("The optional is empty"));

Enter fullscreen mode Exit fullscreen mode

In the above example we can see that the method will print the value if it is present else it will print "The optional is empty".

Till now we have various methods that help us in checking whether the value is present or not bu what if we want to use the value.

1.get()

NEVER USE THIS METHOD

The get() as the name suggest tries to get the value from the Optional and if the value is not present it will throw a NoSuchElementException.

Using this method after checking if the value is present or not is still considerable but using it without even checking defeats the whole purpose of using the Optional in the first place.

The method structure is as follows

 public T get() {
   if (value == null) {
        throw new NoSuchElementException("No value present");
      }
        return value;
    }
Enter fullscreen mode Exit fullscreen mode

Methods that can be used instead of get() if you just want to access the value inside the Optional are

2.orElse()

One of the way is that we can provide a default value to be used in case no value is found inside the Optional.

The method structure is as follows

    public T orElse(T other) {
        return value != null ? value : other;
    }
Enter fullscreen mode Exit fullscreen mode

As the name suggests the method orElse() will return the value if a value is present inside the Optional if no value is present we can provide a default value i.e. "other" parameter to be returned.

Let's look at an example on how to use orElse()

 final Optional<Object> emptyOptional = Optional.empty();

 System.out.println(emptyOptional.orElse(10));
System.out.println(emptyOptional.orElse(generateRandomNumber()));
Enter fullscreen mode Exit fullscreen mode

In the above example the first output will display 10 as the Optional we used is an emptyOptional so the default value we passed in orElse() will be used and for the second print statement a default value will be provide from the method "generateRandomNumber()".

Another way we can get value without worrying about "NoSuchElementException" is

3.orElseGet()

The method orElseGet() is very similar to the method we saw above orElse(). We can provide both a default value directly as well call a function to provide us with a value.

The method structure is as follows

    public T orElseGet(Supplier<? extends T> supplier) {
        return value != null ? value : supplier.get();
    }
Enter fullscreen mode Exit fullscreen mode

As we can see that the method orElseGet() takes in a Supplier as an input which is used in cases where Optional does not contain any value.

The difference between the structure of orElse() and orElseGet() is clear that orElse method accepts a generic parameter T where as orElseGet method accepts a supplier.

Let's look at an example on how to use orElseGet()

 final Optional<Object> emptyOptional = Optional.empty();

  System.out.println(emptyOptional.orElseGet(() -> 10));
  System.out.println(emptyOptional.orElseGet(() -> generateRandomNumber()));
Enter fullscreen mode Exit fullscreen mode

In the above example the first output will display 10 as the Optional we used is an emptyOptional so the default value we passed in orElseGet() will be used and for the second print statement a default value will be provide from the method "generateRandomNumber()".

The difference between orElse() and orElseGet()

We were able to see that on the surface both the methods look and works almost the same but structure wise orElse uses the generic parameter where as the orElseGet uses the Supplier.

The major difference between the two method which impacts the performance is when we use orElse even in the cases where the default value is not used it will be created and stored in memory but in case of orElseGet it will only be created if and when needed.

If the default value is a simple int it may not such a big difference but imagine the cases where a method is being called it will not only take a place in call stack but also has the ability to affect the performance.

Another case that may be possible is when we do not want to provide a default value but we want to throw an error in case a value is not present in Optional.

4.orElseThrow()

There are 2 different variants of orElseThow

1.Default variant

Introduced in Java 10 the default variant of orElseThrow works similar to how get() works i.e. if will check whether an element is present or not and if an element is not present it will throw and "NoSuchElementException".

The structure is as follows

  public T orElseThrow() {
     if (value == null) {
        throw new NoSuchElementException("No value present");
      }
     return value;
    }
Enter fullscreen mode Exit fullscreen mode

The difference between theget() and orElseThrow() method is that even though internally both will throw the same "NoSuchElementException" exception when a value is not present, the person going through the code will be able to understand that method orElseThrow can throw an error by just looking at its name where as in the method get it will be a side-effect.

Let's look at an example on how to use orElseThrow()

    final Optional<Object> emptyOptional = Optional.empty();

    emptyOptional.orElseThrow();
Enter fullscreen mode Exit fullscreen mode

In the above example we can see that an "NoSuchElementException" will be thrown.

2.Parameterized variant

Introduced in Java 8, in this variant we can define the error we want to throw if no value is present inside the Optional.

The structure is as follows

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
Enter fullscreen mode Exit fullscreen mode

Let's look at an example on how to use orElseThrow()

   final Optional<Object> emptyOptional = Optional.empty();

    emptyOptional.orElseThrow(IllegalStateException::new);
    emptyOptional.orElseThrow(() -> new IllegalStateException("Data Not Found"));
Enter fullscreen mode Exit fullscreen mode

In the above example we can see that an "IllegalStateException" will be thrown if value is not present in Optional.

We can either create an exception with default constructor or we can also pass in a message.

Now we have gone through

  • How to create an Optional
  • How to check if an Optional contains any value or not
  • How to proceed if an Optional has a value
  • How to provide a default value

What if we want to transform a value inside an Optional

For the purpose of transformation Optional provides us with various functions such as map, flatmap and filter.

When To Use an Optional

1.Return Value
Optional is suitable to be a return value where expected that a value may or may not be present.

For example the findById method or the findFirst method.

When Not To Use an Optional

1.As a parameter

I have seen some cases where someone has used Optional as a parameter of a method

For example

  public void function1(Optional<Integer> val){
    if (val.isPresent()){
      function2(val);
    }else{
      function3();
    }
  } 
Enter fullscreen mode Exit fullscreen mode

In the example above we can see that optional is being misused.

Issues in the above example:

  • Single Responsibility Principle is being violated
  • The parent function should have handled the choice instead of the child function
  • Why would you want to check if a parameter exists or not, instead why not create 2 separate functions for each use case

2.Wrapping a collection such as ArrayList in Optional

I have seen some cases where an ArrayList is being passed wrapped inside an Optional and later is being checked where it is empty or not

I don't know why would someone do it, if you want to check a collection is empty or not than there are already methods created for it so why would you need to wrap a collection in Optional.

Let me know if you have some more use cases of when and when not use an Optional.

See you in the funny papers 🚀

Discussion (0)