Java 8 Features in a nutshell - Optional

Hope everybody already know that Java 8 is out. It brings a lot of interesting features to make Java easier and more comfortable to use. I will try to talk through some of those features in a nutshell.

Lets start with Optional class. As with all Java 8 features, Optional existed before the release, for example in Guava libraries. Optional class allows you to handle null checks in really nice way. Let’s look at the code. Say we have a User which can have a cat and lets say we have a map of two users Alice and Bob and Alice has a cat:

public static final String BOB = "Bob";
public static final String ALICE = "Alice";
public static final String FLUFFY = "Fluffy";

public class User {
    public final Cat cat;
    public final String name;

    public User(Cat cat, String name) {
        this.cat = cat;
        this.name = name;
    }

    public Cat getCat() {
        return cat;
    }
}

public class Cat {
    public final String name;

    public Cat(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

private Map<String, User> createMap() {
    Map<String, User> result = new HashMap<>();

    User bob = new User(null, BOB);
    User alice = new User(new Cat(FLUFFY), ALICE);

    result.put(BOB, bob);
    result.put(ALICE, alice);

    return result;
}

Now image all you have is a map and you want to get person’s cat name:

@Test(expected = NullPointerException.class)
public void oldNpe() {
    Map<String, User> map = createMap();

    assertEquals(map.get(ALICE).getCat().getName(), FLUFFY); // OK

    String catName = map.get(INVALID_KEY).getCat().getName();   // NPE

    // Have to do:
    User user = map.get(INVALID_KEY);
    if (user != null) {
        Cat cat = user.getCat();
        if (cat != null) {
            catName = cat.getName();
        }
    }
}

You can just lookup key in the map, get User object and get it’s cat and here you go. Unfortunately, couple of calls in this line can return null:

  1. Map.get() can return null
  2. User.getCat() can return null

To overcome that you will have to write two nested if statements as shown on lines 10-16. Quite a lot of code for such simple tasks, right? Imagine if Cat would also have it’s favourite food as well, which can have a brand and you need to get it. Then you will be doomed to add another if and spend end of your life trying to understand how to format in sensible way 4 closing brackets.

Fear not fellow Java developer! Optionals to the rescue! Lets create helper method which will get person’s cat name in NPE-safe way:

private Optional<String> getCatName(String key, Map<String, User> map) {
    return Optional.ofNullable(map.get(key))
            .map(User::getCat)
            .map(Cat::getName);
}

Now, let me explain what is happening here:

On line 2 we are wrapping result of map.get() function to Optional object. Optional class is class used to potentially hold some object. It has 3 methods which we touch here indirectly:

private <T> Optional<T> map(Optional<String> optional, Function<String, T> func) {
    if (optional.isPresent()) {
        return Optional.ofNullable(func.apply(optional.get()));
    } else {
        return Optional.empty();
    }
}

Now, lets get back to getCatName function. Once we got first Optional<User> we apply map function using lambda expression. I will cover them in another blog post, for now you need to understand that it is basically tells to call User::getCat() method BUT on User instance object if Optional<User> is not empty. Line  3 will return Optional<Cat> and we use same trick again to get Cat name, which is returning result as Optional<String>.

Now, we can demonstrate how to use this method:

@Test
public void optionalSavesNullCheck() {
    Map&lt;String, User&gt; map = createMap();

    Optional&lt;String&gt; aliceCatName = getCatName(ALICE, map);

    assertTrue(aliceCatName.isPresent());   // Alice has a cat
    assertEquals(aliceCatName.get(), FLUFFY);

    Optional&lt;String&gt; bobCatName = getCatName(BOB, map);

    assertFalse(bobCatName.isPresent());    // Bob does not have a cat
    assertFalse(getCatName(INVALID_KEY, map).isPresent());  // non-existent user does not have cat either
}

As you can see, final code is much more clear than original if madness and it deliver same functionality and no NPE.

The “trick” here is that we never return null, we always return Optional object which holds information about previous operation results and allows itself to be used in subsequent operations.

I hope this blog post helped you understand hew Optional class in Java 8. If you have any questions, please use comment form below.