Optional class were introduced in order to prevent NullPointerException, but method get() used to retrieve the value inside the Optional might still throw a NoSuchElementException.

Different name, same issue?

Calling get() without checking that the value is actually present it’s a problem. So we should always write something like this in order to use get().

Optional<String> myString = Optional.ofNullable(nullableString());
   if(myString.isPresent()){
       doSomething(myString.get());
   }

But are Optional really meant to be used in this way? Actually, no.

Writing block of isPresent/get is not so different from writing a classic null check.

String myString = nullableString();
   if(myString != null){
       doSomething(myString);
   }

How can we really benefit from Optional object?

1. Optional orElse example

It returns the value if is present, or the other specified otherwise.

Let’s see an example:

@Test
public void namePresent(){
  Optional<String> petName = Optional.of("Bobby");

  assertEquals("Bobby", petName.orElse(""));
}
@Test
public void orElse(){
  Optional<String> petName = Optional.empty();

  assertEquals("", petName.orElse(""));
}

As you can see the code is much more readable and correct compared to the isPresent/get version:

String petName = "";
if(petNameOptional.isPresent()){
    petName = petNameOptional.get();
}

2. Optional orElseThrow example

It returns the value if it is present, or throws the specified exception otherwise.

@Test
public void namePresent(){
  Optional<String> petName = Optional.of("Bobby");

  assertEquals("Bobby", petName.orElseThrow(IllegalArgumentException::new));
}

@Test(expected=IllegalArgumentException.class)
public void throwException(){
  Optional<String> petName = Optional.empty();

  petName.orElseThrow(IllegalArgumentException::new);
}

3. Optional filter example

filter() is useful to specify other conditions on our object. It returns an Optional containing the value if it is not empty and satisfies the specified predicate, an Optional.empty() otherwise.

In this example we want that the name length should be greater than 3.

@Test
public void valueNotFilteredOut()
{
  Optional<String> petName = Optional.of("Bobby")
                                     .filter(name -> name.length() > 3);
  assertEquals(Optional.of("Bobby"), petName);
}

@Test
public void valueFilteredOut()
{
  Optional<String> petName = Optional.of("Bob")
                                     .filter(name -> name.length() > 3);
  assertEquals(Optional.empty(), petName);
}

4. Optional map example

map() is a method used to apply a transformation to the content of the Optional if it’s present.

In this example we want to transform the name of the pet from String to Int by applying String.length() function.

@Test
public void transformWhenValueIsPresent()
{
  Optional<Integer> nameLength = Optional.of("Bobby")
                                         .map(String::length);
  assertEquals(Optional.of(5), nameLength);
}

@Test
public void mapNotExecutedWhenEmpty()
{
  Optional<Integer> nameLength = Optional.<String>empty()
                                         .map(String::length);
  assertEquals(Optional.empty(), nameLength);
}

What’s interesting with the map method is that we are able to declare sequentially our operation without worrying if the value is present or not. We can focus on the success case, we don’t need to branch our code, the scenario in which the value is not present is automatically handled by the abstraction Optional that we are using.

5. Optional flatMap example

flatMap() it’s similar to map() but should be used when the transformation function returns an Optional of some <T>. flatMap() executes the transformation function like the map() but instead of returning Optional<Optional<T>> if will just return Optional<T>. Basically it flattens the Optional.

Let’s clarify it with an example. We define a function that returns an Optional, let’s say a function that returns the last gift received by the pet.

private Optional<String> lastGiftReceivedBy(String petName) = ???

If we call our lastGiftReceivedBy function in a map() we will have an Optional<Optional<String>>.

Optional<Optional<String>> lastGiftReceived = Optional.of("Bobby")
                                                      .map(petName -> lastGiftReceivedBy(petName));

In order to flatten them we can use flatMap()

Optional<String> lastGiftReceived = Optional.of("Bobby")
                                            .flatMap(petName -> lastGiftReceivedBy(petName));

Writing this solution by using isPresent/get would have meant using a nested if: one for the first Optional, one for the other.

6. Optional ifPresent example

IfPresent, that it’s different from isPresent, accept a function, a Consumer, and executes it only if the value is present.

Basically ifPresent() should be used for function that does side-effect (returning a void).

Instead of writing something like:

if(optional.isPresent){
  doSomething(optional.get)
}

We can write:

optional.ifPresent(val -> doSomething(val))

Let’s say we want to do a System.out.println() of the name:

Optional.of("Bobby")
        .ifPresent(name -> System.out.println(name));

Resources:

JavaDoc

Short Tutorial By Example