Using the Optional Feature in Java 8

Nov 18, 2019
hackajob Staff

At hackajob HQ, one of our favourite features in Java 8 is the Optional Feature. Helping to avoid unexpected errors and write clean code, in today’s article, we’ll be covering it in detail.

What is the Optional Feature?

As per the API documentation, ‘Optional is a container object which may or may not contain a null value’. In a nutshell, it’s used to represent a value that can either be present OR absent. It’s important to note that methods return a null value many times. If the code that invokes the method expects a non-null value, this can result in a ‘NullPointerException’. To avoid this, you’ll need to add explicit null checks in your code, which can make the overall script difficult to read.

Thankfully, Java 8 Optionals can help in avoiding this boilerplate code for null checks. In fact, using an 'Optional' instead of explicit 'null checks' will result in clean code. Result!

Creating an Optional

There are three static methods on the 'Optional class' which can be used to create an 'Optional', which we’ve detailed below:

Optional.empty

'Optional.empty' can be used to create an empty 'Optional' (in other words, an 'Optional' without a value). The following code demonstrates this:

Optional<Integer> opInt = Optional.empty();

The above code creates an 'Optional' without a value.

Optional.of

The 'Optional.of' method can be used to create an 'Optional' with a non-null value, with the following code showcasing how this works:

Optional<String> opStr = Optional.of("Hello World");

This code creates a ‘String Optional’ that corresponds with the string, “Hello World”.

If you try to use the ‘Optional.of’ method to create an ‘Optional’ with a null value, a ‘NullPointerException’ occurs. The following code demonstrates this:

Optional<String> opStr = Optional.of(null);

This example above results in a ‘NullPointerException’.

Optional.ofNullable

The ‘Optional.ofNullable’ method can be used to create an 'Optional' that can hold both a null AND non-null value.

When the 'ofNullable' method is invoked with a value, it returns an 'Optional' with that value. The following code showcases how this works:

Optional<String> opStr = Optional.ofNullable(“Hello World”);

The above example creates a ‘String Optional’ that corresponds to the String “Hello World”.

When the ‘ofNullable’ method is invoked with a null, it returns an empty ‘Optional’. The following code demonstrates this:

Optional<String> opStr = Optional.ofNullable(null);

This code creates an empty optional.

Methods in Optional

There are several methods in the ‘Optional’ class that can be used to check whether there’s a value in the ‘Optional’, to retrieve a value in the ‘Optional’ and to perform a specified action on the 'Optional'. We’ve elaborated some of the methods in the ‘Optional’ class in the subsequent sections.

Optional.isPresent

The 'Optional.isPresent' method can be used to check if a value is present within the 'Optional' object. If a value is true, it returns a present, otherwise it returns a false. The following code demonstrates this:

Optional<String> opStr = Optional.ofNullable("Hello World");

System.out.println("Present:"+opStr.isPresent());

Since the value “Hello world” is present in the 'Optional opStr’, the code prints the following output:

Present:true

Optional.ifPresent

The ‘Optional.ifPresent’ method performs an action on the value in the ‘Optional’ if present, otherwise it does nothing. The action to be performed is specified via a ‘Consumer instance’.

The following code demonstrates this:

Optional<String> opStr = Optional.ofNullable("Hello World");

opStr.ifPresent(str-> System.out.println(str));

In the example above, an ‘Optional String opStr’ is created with the text “Hello World”. A lambda expression is specified in the ‘ifPresent’ method that simply prints the input value. So this code prints the following:

Hello World

However, if a null 'Optional' is used with the ‘ifPresent’ method it does nothing as demonstrated below:

Optional<String> opStr = Optional.ofNullable(null);

opStr.ifPresent(str-> System.out.println(str));

When this code is executed, no output will be printed to the console.

Optional.get

The ‘Optional.get’ method returns the value in the ‘Optional’ if present and if not, it throws a ‘NoSuchElementException’ exception. The example below shows how this works:

Optional<String> opStr = Optional.of("Hello World");

String str = opStr.get();

System.out.println(str);

In this case, the 'Optional opStr' has the value “Hello World”. So this code prints the following output:

Hello World

However, if an empty 'Optional' is used an ‘Exception’ occurs as demonstrated below:

Optional<String> opStr = Optional.ofNullable(null);

String str = opStr.get();

System.out.println(str);

This code prints the following output:

Exception in thread "main" java.util.NoSuchElementException: No value present

Optional.orElse

The ‘Optional.orElse’ method returns the value in the ‘Optional’ if present, otherwise it returns the specified value. The following code demonstrates this:

Optional<Integer> opNum = Optional.ofNullable(5);

Integer num = opNum.orElse(1);

System.out.println(num);

In this case, the ‘Optional opNum’ has the value 5 and so this code prints the following output:

5

However, if a null ‘Optional’ is used with the ‘orElse’ it will return the specified value as demonstrated below:

Optional<String> opStr = Optional.ofNullable(null);

Integer num = opNum.orElse(1);

Here, the value 1 is specified with the ‘orElse’ method so this code prints the following:

1

Optional.orElseThrow

The ‘Optional.orElseThrow’ method returns the value in the ‘Optional’ if present, otherwise it throws the specified exception. The following code demonstrates this:

Optional<Double> opDouble = Optional.of(4.5);

Double value = opDouble.orElseThrow(RuntimeException::new);

System.out.println(value);

Here, an ‘Optional opDouble’ is created with the value 4.5 which is returned by the ‘orElseThrow’ method. So this code prints the following value:

4.5

However, if an empty ‘Optional’ is used with the ‘orElseThrow’ method, it throws the specified exception as demonstrated below:

Optional<Double> opDouble = Optional.empty();

Double value = opDouble.orElseThrow(RuntimeException::new);

Here, a ‘RuntimeException’ is specified in the ‘orElseThrow’ method, with the code printing the following output:

Exception in thread "main" java.lang.RuntimeException

Optional.filter

The ‘Optional.filter’ method applies a condition specified via a ‘Predicate’ instance to the value in the ‘Optional’. If a value is present in the ‘Optional’ and the condition is satisfied, it will return an ‘Optional’ with a value. If this isn’t the case, it will return an empty ‘Optional’. The following code demonstrates this:

Optional<Integer> opInt = Optional.of(6);

Optional<Integer> opIntTrue = opInt.filter(num -> num > 3);

System.out.println(opIntTrue.get());

Here, an ‘Optional’ integer ‘opInt’ is created with the value of 6. A lambda expression is specified in the ‘filter’ method that checks if the number is greater than 3. The ‘filter’ method simply executes this lambda expression and since the condition is true, it returns an ‘Optional opIntTrue’ with the value of 6. This code prints the following output:

6

If the condition in the filter method is false, it returns an empty ‘Optional’ as demonstrated below:

Optional<Integer> opIntFalse = opInt.filter(num -> num < 3);

System.out.println(opIntFalse.isPresent());

So this code prints the following output:

false

Optional.map

The ‘Optional.map’ applies the specified ‘Function’ to the value in the ‘Optional’ and if the result isn't null, it returns an ‘Optional’ with the result. Otherwise, it’ll return an empty ‘Optional’. The following code demonstrates how this works:

Optional<String> opString = Optional.of("Hello World");

Optional<Integer> opLength = opString.map(str -> str.length());

System.out.println(opLength.isPresent());

System.out.println(opLength.get());

Here, an ‘Optional String’ (otherwise known as ‘opString’) is created and corresponds to “Hello World”. A lambda expression is specified in the ‘map’ method that returns the length of the string. The ‘map’ method applies this lambda expression to the string in the ‘Optional’ and returns its length as an ‘Optional Integer’ (otherwise known as ‘opLength’). This code will print the following:

true

11

If the 'map' method is applied on an empty ‘Optional’, it will return an empty ‘Optional’ as demonstrated below:

opString = Optional.ofNullable(null);

opLength = opString.map(str -> str.length());

System.out.println(opLength.isPresent());

So this code prints the following output:

false

Reminder: How Optional is Beneficial

As we mentioned earlier, the main advantage of ‘Optional’ is to avoid the boilerplate code involved with writing explicit null checks.

Pre-Java 8

Consider the following code that demonstrates how a null check was required prior to Java 8:

Address address = new Address("street","city","country");

Employee employee = new Employee("Jane","Doe",address);

//some code

Address address2 = employee.getAddress();

if(address2 != null){

System.out.println(address2);

}

Essentially, you’ll need to explicitly check if there's an ‘Address’ object within the ‘Person’ class. If this check is not included and the address object is ‘null’, the code will throw a ‘NullPointerException’.

Using Java 8 Optional

Using ‘Java 8 Optional’, the above code can be re-written like so:

Optional<Address> address = Optional.of(new Address("street1","city1","country1"));

Employee employee = new Employee("Jane","Doe",address);

Optional<Address> address3 = employee.getAddress();

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

Compared to the Pre-Java 8 code, this new version is so much easier to read. In fact, the ‘Optional’ feature gets rid of the boilerplate code that is necessary for null checks.

Other Optional Classes

In addition to the generic ‘Optional’ seen above, Java 8 has also added the following classes in order to handle ‘Optional data’ of a specific type:

·       OptionalInt – An ‘Optional’ which may or may not contain an ‘int value’

·       OptionalDouble - An ‘Optional’ which may or may not contain a ‘Double value’

·       OptionalLong  – An ‘Optional’ which may or may not contain a ‘Long value’

Optionals are a really important feature in Java 8 and should be utilised accordingly. Helping to design better code, it’s worth noting that the method signature itself can now convey that a method can return a null value, which allows the caller of the method to determine how best to deal with it.

Have you tried using the Optional feature in Java 8? Let us know on Twitter. In the meantime, check out our blog and discover a wealth of tech tutorials.