Optional in Java

Understanding Null Pointer Exception & Use of Optional to minimize it.

April 9, 2020

In this blog, we will be discussing Optional which is a feature of java 8 and understand its usage.
let us first list the contents which we will be discussing in this blog.

  • Dangers of Null
  • What is NullPointerException and how it is handled by other languages.
  • What is Optional, its benefits and why it was introduced in JAVA 8.
  • How to create Optional, how to get the value back from optional, and the important methods that can be used with optional.
  • What are the do’s and dont’s practices to be followed with Optional

The creator of null reference said that creating a null was a billion Dollar Mistake! And Indeed, it is, in some ways.

Dangers Of Null

A null reference can be a danger if not handled with care. If a value or object is null, any operation on that null reference causes NullPointerException. It is, the most occurred exception. We frequently get attacked by it.

Let’s  see an example to explain the danger of null. This example clearly explains the challenge with a null.

This is a nested object structure for a Computer. Computers contains sound card , sound card contains a USB in it. To get the version of usb :

NULLPOINTEREXCEPTION

String version = computer.getSoundcard().getUSB().getVersion();

This code looks pretty reasonable. However, many computers (for example, the Raspberry Pi) don’t actually ship with a sound card. So what is the result of getSoundcard()?

A common and a bad practice is to return the null reference to indicate the absence of a sound card. Unfortunately, this means the call to getUSB() will try to return the USB port of a null reference, which will result in a NullPointerException at run time and stop the program from running further.

What can be done to prevent unintended null pointer exceptions?

One way is we can add checks to prevent null dereferences as shown below

String version = "UNKNOWN";
if(computer != null){
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null){
    if(usb != null){
      version = usb.getVersion();
    }
  }
}

Unfortunately, we need a lot of boilerplate code to make sure we don’t get a NullPointerException. In addition, it’s just annoying that these checks get in the way of the business logic. In fact, they are decreasing the overall readability of our program. So a better way is needed to model the absence and presence of a value.

How NULLPOINTEREXCEPTION is handled by other languages

Languages such as Groovy have a safe navigation operator represented by “?.” to safely navigate through potential null references. Not only Groovy, Other functional languages, such as Haskell and Scala, take a different view.

Haskell includes a Maybe type, which essentially encapsulates an optional value. A value of type Maybe can contain either a value of a given type or nothing. There is no concept of a null reference.

Scala has a similar construct called Option[T] to encapsulate the presence or absence of a value of type T. Then it has to be explicitly checked whether a value is present or not using operations available on the Option type, which enforces the idea of “null checking.” .

How JAVA deals with this

Let us see how JAVA deals with the situation of null pointer exception. 

We will discuss the following operations

So, Java SE 8 introduced a new class called java.util.Optional that is inspired from the ideas of Haskell and Scala.

The object or value is wrapped in an Optional and then used.

The code using JAVA 8 Optional will be like follows

public class Computer {
  private Optional<Soundcard> soundcard;  
  public Optional<Soundcard> getSoundcard() { ... }
  ...
}

public class Soundcard {
  private Optional<USB> usb;
  public Optional<USB> getUSB() { ... }

}

public class USB{
  public String getVersion(){ ... }
}

Looking at the code we observe that Computer contains sound card and it is wrapped in an Optional of type soundcard. It may be present or may be empty. Similarly Soundcard contains USB. USB is wrapped in optional of type usb so it may be empty.

So every item wrapped in optional is allowed to be missing.
So ,we will not be needing the following null checks that we were having everywhere to avoid NullpointerException.

String version = “UNKNOWN";

if(computer != null){

  Soundcard soundcard = computer.getSoundcard();

        if(soundcard != null){

          USB usb = soundcard.getUSB();

                      if(usb != null){
                            
                     version = usb.getVersion();
   
                      }
         }
}


Optional itself is enough to say that this value can be null.

Basically, Optional hints the Consumer of that Object that it can be null so it must be dealt properly. And for that Optional class includes methods to explicitly deal with the cases where a value is present or absent.

Now let us see how a value is wrapped in an Optional?

import java.util.Optional;
public class OptionalCreation {
 
     public static void main(String[] args() {
          String val = "A String";
          Optional<String> optional = Optional.of("val"); 

          //to get emptyOptional
          Optional <Integer> empty  = Optional.empty(); 

           //nullable.
          Optional <String> nullable = Optional.ofNullable(val); //Optional may     have a value.  
          Optional <String> emptyOptional = Optional.ofNullable(null); //Optional may have a null or a value. 
    }
}

One important point to note here is an optional having null always means empty optional.
If we are wrapping null into optional that optional is called empty optional or vice versa.

So Optional is a Box which wraps a value in it.

  • It consumes 16 bytes
  • Its a separate Object That means that it is not true that no new object will be created when we wrap a value into optional
  • Optionals are Immutable. Their value can not be changed once they’re created.

By the above discussion we know that creating Optional everywhere can cost performance . So, we should not replace null with Optional where it is not really needed..

Till now we have talked about what is Optional and how to create optional.
We create optional, then we apply all the operations on that optional without bothering about NullPointerException and then we unwrap or get back the result from that optional. The main point of Optional is to provide a means for a function returning a value to indicate the possible absence of a return value


Now, we must know how to use them. So there are methods in Optional class that are used to perform various operation and also to get the value back.
When a chain of operations is created, that chain might need wrapping and unwrapping of value in optional multiple times.

So first let will discuss methods related to get the value back from optional .

import java.util.Optional;
public class Unwrap {
      public static void main (String[] args) {
      Integer a = 10;
     Optional<Integer> optional = Optional.of(a);
     Integer val = optional.get();     //get() is used to get the value back
     System.out.println(val);
   }
}

Now an important point to note here is we shall never use get() method if we are not sure that whether optional is having a value.

So if we have an empty optional and we try to get a value from it without knowing that it is an empty Optional

Optional<Integer> emptyOptional = Optional.empty();
emptyOptional.get();

when we compile this code we get a NoSuchElementException

Does this mean that the method should never be used. Then what is the solution to this problem?

The solution is we have a method isPresent() in Optional class that can help us use this method. isPresent() methods returns a boolean. If optional is empty it returns false else true. The syntax is

optional.isPresent() ? optional.get() : "Default" ;

This means that if there is a value in optional then execute the get to get the value back otherwise return Default.

If we display the value here

Integer val = optional.isPresent() ? optional.get() : 0  ;
system.out.println (val);

This returns the value 10.

But, we have more and better alternatives to do so We have orElse() or orElseGet() methods

orElse() Method

We use orElse() to set a default if the value is absent.

Integer orElse = optional.orElse(0);
System.out.println(orElse);

It returns 10.

If this function is invoked on emptyOptional

Integer orElse = emptyOptional.orElse(0);
System.out.println(orElse);

It returns the default value which is 0. Thus It returns the alternate value instead of throwing an exception.

orElseGet() method :

It also sets the default but it takes a supplier.

emptyOptional.orElseGet (() -> 0 );
System.out.println(orElseGet);

This also returns 0.

So if we consider the performance, there is one more difference between these two methods

In case of orElse(), Default object always gets generated however it gets returned only if Optional is empty which may have performance overhead whereas orElseGet() executes the supplier to get the default ONLY in case Optional is empty .If its not empty, it directly returns the present value without executing the supplier function. So this is the way we can get the values back.
So it is preferable to use alternatives for optional.isPresent() to return something else if optional is empty.

Now one more scenario is in case of empty optional, if we want no return value , but we still want a specific exception, then also there is method orElseThrow() which lets us do so.

orElseThrow() Method

orElseThrow() returns value if the value is present else it throws specified Exception, so it takes an Exception Supplier.

Integer orElseThrow = emptyOptional.orElseThrow(() -> new IllegalArgumentException());

So we get the Exception supplied by the Supplier. And then in java 10 there is one more overloaded version of orElseThrow without any Input arguments:

orElseThrow()
If a value is present, it returns the value, otherwise it throws NoSuchElementException.

So to summarize we discussed the following four methods and these were:

get() : To get the value back from optional.which is a risky method, if optional is empty it throws exception.

isPresent() : To check whether the optional is empty or not

orElse() : It is an alternative for get(), it returns the value if optional is having value else it returns the default value supplied to it.

orElseGet() : Same as orElse except it takes a supplier function to return the default value.

orElseThrow() : To throw specific exception if no value in optional. And no arg orElseThrow throws NosuchElementException.

Optional : Important Methods

Now let us talk about the important operations that can be performed on an optional. We will first discuss the following three operations

  • map()
  • filter()
  • flatMap()

map() :

If the value is present, it transforms or maps the value into another optional according to the given function and return the result again in optional otherwise returns an empty optional.
So, It takes a function.

We can use it like this :

Optional<String>map = optional.map(val ->"Replaced"); 
system.out.println(map.get());

It returns the transformed value i.e “Replaced”.

If the Optional is empty and we are invoking map on it than we get an Exception.

We can also use :: operator for providing function like

return optional.map(ClassName :: MethodName); //this method should take something and return something

So as it again returns an optional. This can further be chained with orElse() or orElseGet() methods..

return optional.map((v) -> "Replaced").orElse("NoValueToReplace");

So if the optional returned by map is empty, it will return this String..

filter()
Map modifies the value of optional .and filter checks the condition on optional. So, it takes a predicate. If condition return true, it returns the same optional else it return an empty optional.
Also if optional is empty it returns the empty optional back.

return optional.filter((v) -> v.equals("BasicsStrong")).orElseThrow(IllegalArgumentException :: new);

flatMap() :

This method is similar to map, but the mapping function is one whose result is already an Optional, and if invoked, flatMap does not wrap it within an additional Optional.
Consider that suppose we have an optional of optional

Optional> optOpt = Optional.of(Optional.of(10));
optOpt.flatMap(op -> op.map(val -> val+ 2));

This optOpt is holding an Optional so that will be the parameter to the function and then suppose we want to modify the value then again we can perform map on the optional and can modify the value , this map will wrap the value in Optional and again return this new optional and the filterMap will take this Optional and return it directly without doing anything.
so in flatMap returned object is already an optional, it does’t need to again wrap the modified value again in an optional.

Now let us discuss few more important methods from Optional class.

  • ifPresent(Consumer)
  • ifPresentOrElse(consumer, runnable)
  • stream()
  • or(supplier)
  • equals​(object)
  • hashCode()

ifPresent()

If value is present, it executes a consumer on that value otherwise does nothing.

return optional.ifPresent()

ifPresentOrElse()

It is used for executing some task on optional. It takes a consumer, if optional have value then executes that consumer on that value else executes the emptyAction or runnable.

void optional.ifPresentOrElse(Consumer, Runnable)
    opt.ifPresentOrElse(action, emptyAction);

Stream Optional.stream():
This was introduced in JAVA 9. If a value is present, returns a sequential Stream containing only that value, otherwise returns an empty Stream.

Stream stream = opt.stream();
Stream> st = optOpt.stream();
Stream sm = st.map(op -> op.orElse(0));

This will return a Stream having the value return by this map.

or()

It takes a supplier and returns the same optional if optional has a value and if optional is empty then it supply new optional returned by the supplier function.

optional.or(Supplier> supplier);

The important point to note here is that the supplying function is not null or should not produce a null result, because then it will result to NullPointerException.

equals()

<strong>public boolean equals​(Object obj) :</strong>

It is used for checking some other object is “equal to” this Optional or not.

The other object is considered equal if it is also an Optional and
either if both instances have no value present, the present values are “equal to” each other via equals().

hashcode()

<strong><br></strong>public int hashCode() :

It returns the hash code of the value, if present, and otherwise 0 (zero) if no value is present. Thus It Overrides hashCode in class Object

So these were the important methods or operations we can perform on optionals.

We hope that after reading this blog you have clearly understood the NullPointerException and the Optional class creation and the methods of Optional Class.

For more concepts of JAVA , Keep Reading and Stay Connected !!