JavaJava 8Technology

Avoid NullPointerException using Java 8 Optional

In this Java 8 Optional Tutorial you will learn How to avoid Nulls and Null Pointer Exceptions and avoid Null Checks using Java 8 Optional.

What is Null and NullPointerException ?

Java by-default assigns null to uninitialised object reference variables. In other words, when you declare reference variables, and do not initialise them, java assign them a special value called as null.

public class TestClass {
    String string1;
    String string2 = null;

    public static void main(String[] a){
        TestClass testClass = new TestClass();

        System.out.println(testClass.string1);    // Output: null
        System.out.println(testClass.string2);    // Output: null
    }
}

Why NullPointerException ?

In Java the reference variables are not objects. But, reference variables are just handle to the objects. Although you use reference variables to access a member, you actually access them through objects. Hence, when you try to access a member on an un-initialised reference variable, you get a NullPointerException.

In Java a program can assign a reference variable to an object at runtime. Therefore it is not possible at compilation time to know if a certain reference is going to be null. Hence the NullPointerExceptions are runtime exceptions and difficult to foresee.

For instance, you are writing an UserService, whose getUsers method returns List of Users. The User and the referred Address class look like below.

public class User {
    private String name;
    private String lastName;
    private Address address;
   
    ...
}


class Address {
    private String address;
    private String suburb;
    private String state;
    
    ...
}

Now, the consumer of UserService wants to print the state of first User from the list. For that, it will extract the nested objects.

List<User> users = service.getUsers();

// Possibility of NullPointerException
System.out.println("State is " + users.get(0).getAddress().getState());

Here, you have introduced possibility of NullPointerExceptions. Below are the possibilities:

  1. Entire list is null.
  2. First User from the list is null.
  3. Users Address is null.

In any of the cases your code is going to throw a NullPointerException. The exception can be avoided by adding Null checks.

Null Checks to avoid NullPointerException

To avoid the NullPointerExceptions, you need to add null checks in your code. For example, refer to the modified example below

if (users != null) {
    
    User user = users.get(0);
    if (user != null) {
        
        Address address = user.getAddress();
        if (address != null) {
        
            String state = address.getState();
            if (state != null) {
                System.out.println("State is " + state);
            }
        }
    }
}

Finally, we have avoided all the potential NullPointerExceptions and the code is now safe. However, that made the method ugly. Moreover, think about the case where there are more nested objects like this.

What is Optional ?

Java 8 introduced an Optional class which is a nicer way to avoid NullPointerExceptions. You can use Optional to encapsulate the potential null values and pass or return it safely without worrying about the exception.

Without Optional, when a method signature has return type of certain object. The user often tend to assert the existence of the object. Hence, the possibility of null is often ignored.

The Optional, clearly indicates method behaviour. For example, below method signature clearly indicates that it may or may not return the list of Users.

// Indicates possibility of null result
Optional<List<User>> getUsers();

// Doesn't indicate that null may be returned
List<User> getUsers();

Also, the Optional improves documentation and makes more sense. For example, you have seen our User class in the above sections. It doesn’t indicate that the Address is an optional field and can be left blank. For instance, we will add Optional in the example and it will be more clear.

public class User {
    private String name;
    private String lastName;
    private Optional<Address> address;
 
    ...
}

How to use Optional ?

Now, let’s see how can you use Optional to check if the contained object is null or not and what actions we can take in either cases.

1. Get Value

The get method simply returns the encapsulated object from optional. In this case, no null check is done and the optional returns the contained value as is.

Optional<Address> optAddress = user.getAddress();
Address address = optAddress.get();
2. Get if Object is Not Null, else Throw Exception

The method orElseThrow is more convient and safe method to use. It returns the object value if the object is not null. However, if the object is null it throws the specified exception.

Optional<Address> optAddress = user.getAddress();

Address address = optAddress.orElseThrow(UserNotFoundException::new)

Note: the UserNotFoundException::new is an example of Constructor Reference.

3. Get if Object is Not Null, else return default

The method orElse is one more interesting method of Optional. It returns the contained value if it is not null. Otherwise it returns the given default value.

Address defaultAddress = new Address (....);
Optional<Address> optAddress = user.getAddress();

Address address = optAddress.orElse(defaultAddress);
4. Check if it is Not Null

The isPresent is a simple boolean check to see if the contained object is null.

Address address;
if(optAddress.isPresent){
    address = optAddress.get();
}
5. Consume if it is not Null

The ifPresent method passes the value to Consumer Function in the form of Lambda Expression.

Optional<Address> optAddress = user.getAddress();

optAddress.ifPresent(System.out::println);

How to create Optional instance ?

Till now you learnt, how to use Optional. Now, in this section we will see what are the ways to create and Optional instance.

1. Empty Optional

There is a static empty method in Optional class that creates an empty optional. For example, if a method has nothing to return it can return an empty optional instance.

return Optional.empty();
2. Optional of Not Null value

If you want to assert that the object to be placed in the Optional is not-null, you can use the static of method. You will get NullPointerException if you try to put a null value in optional using of.

Optional.of(notNullUserList);

//or

Optional.of(new User(...));
3. Optional of Nullable value

Finally, if you want to return a nullable value use the static ofNullable method on Optional.

Optional.ofNullable(user);

//or

Optional.ofNullable(null);

Summary

This was an Introduction to Java 8 Optional. To sum up below are the points you learnt.

  • The NullPointerExceptions in Java occur at runtime and is unexpected.
  • Null checks avoid NullPointerExceptions. However, the code looks ugly.
  • The Optional is an object container provided by Java 8 which encapsulates the object returned by method.
  • Optional has methods to safely access the contained object. Hence avoid null checks and NullPointerExceptions.
  • There are various ways of creating Optional instance and accessing the contained value.

Benefits of Using Optional

  • Avoids Null checks and NullPointerExceptions.
  • Clearly indicates method behaviour (what the method returns).
  • Improves documentation. For example, explicitly indicates optional fields in an object.