Java 8 Optional – Replace your get() calls

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 value is actually present it’s a bug. So we should always write something like that in order to use get().

But are Optional really meant to be used in this way? No.

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

Let’s see how we can 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:

As you can see we haven’t called get() and we’ve made the code easier and more readable compared to the isPresent/get version:

 

2. Optional orElseThrow example

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

 

3. Optional filter example

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

In this example we want that the name not only is different from null but also that is not empty or made of only empty spaces.

And those are the tests for the null and the empty name:

4. Optional ifPresent example

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

So instead of writing something like:

You can write:

or if you prefer:

But let’s have a look to a proper example.

We define a Pojo class, useful also for the following examples, that represents a Loyalty card.

We want to add 3 points to the loyalty card if the loyalty card is actually present.

Node: In the following example we’re going to use Mockito to mock LoyaltyCard class. Don’t worry if you are not familiar with Mockito, I’ll add some comments to the code.

 

5. Optional map example

map() is a method used to transform an input in a different output. In this case, nothing changes except that the map operation will be executed only if the value is actually present, otherwise it returns an empty Optional.

In this example we want to retrieve the number of points of our loyalty card if we have it otherwise, the number of points will return 0.

6. Optional flatMap example

flatMap() it’s really similar to map() but when output is already an Optional it doesn’t wrap it with another Optional. So instead of having Optional<Optional<T>> if will just return Optional<T>.

Let me clarify it using an example. Let’s define a new class, called Gift.

And let’s define a new method to our LoyaltyCard class that returns an Optional containing the last Gift chosen. Since we are going to mock the result of this method, we don’t really care about its implementation.

We can now create a mocked Gift with name “Biography of Guybrush Threepwood”, put it into an Optional and make getLastGift return it. So if we write:

The output will be an Optional<Optional<Gift>> that is not what we want, so flatMap will unwrap this double level and leave only an Optional<Gift>.

Writing this solution by using isPresent/get would have meant using a nested if: one for check that card was present and another of checking the gift. Harder to read, easier to fail.

7. Optional ifPresentOrElse ?

Unfortunately, this is yet to come 🙂 It will be available in Java 9.

Until then we have to write something like:

There are cases in which you are allowed to use get() and isPresent() but use them with a grain of salt.

Resources:

JavaDoc

Short Tutorial By Example

Java 8 Stream – From List to Map

An example to convert a List<?> to a Map<K,V> using Java 8 Stream.

Java 8 – Collectors.toMap()

Let’s define a Pojo class:

In the first example, we convert a List<Person> in a Map<String, Person> that has email as key and the object itself as value.

The output will be:

Or using lambda:

Output:

Let’s break this out

First of all, we create a Stream of Person from the List<Person> defined.

Then we collect this stream in a Map. Java 8 helps us to define the needed Collector by providing us the method: Collectors.toMap().

Collectors.toMap() takes two functions – one for mapping the key and one for the value – and returns a Collector that accumulates elements into a Map.

Since we are working with Stream of Person – our input it’s an object Person.

We have chosen the email as key, so that is a function that given the input – Person – returns its email:

and then the object itself as value, so it’s just an identity function:

These are the parameters for toMap.

Another example

Given a List<Person> we want to create a Map<String, Integer> that contains the name as key and the age as value.

We just need to change the two parameters of the Collectors.toMap, by specifying:

So the code will be:

Output:

If you are a good observer you may have noticed that the order hasn’t been respected. That’s because the default implementation used by toMap is the HashMap that does not guarantee the order of the map.

 

Java 8 – Collectors.toMap with a LinkedHashMap

 

If we want to preserve the order we should use a LinkedHashMap instead of the HashMap.

Let’s try the previous example by passing a LinkedHashMap to Collectors.toMap()

Output:

We are using the definition of toMap that takes four parameters:

  • keyMapper – a mapping function to produce keys
  • valueMapper – a mapping function to produce values
  • mergeFunction – a merge function used to resolve collisions between values associated with the same key
  • mapSupplier – a function which returns a new, empty Map into which the results will be inserted

We’ve already discussed the first two parameters.

In case of a collision we just want to throw an exception, so as third parameter we define that. In the example, we used the same implementation of the static method throwingMerger defined in the java.util.stream.Collectors class.

The fourth parameter it’s the one in which we define a function that returns our LinkedHashMap.