NullPointerException
s are a royal pain in the butt. They can strike at almost any point if your code encounters any “edge cases” that you hadn't previously thought of.
And when a NullPointerException
is thrown, it can severely impact the quality of your user's experience.
For something as simple as “Hey, there's nothing here”, it's amazing how serious a problem this can be.
Well, that's where Java 8 has stepped up their game and finally provided us with a keyword we can use to solve this issue.
[Enter Stage Left]: Optional
The Java 8 Optional
keyword is used as a wrapper around objects (kind of like a container).
In Oracle's tutorial on this keyword they use the example of a soundcard in a computer.
A computer can have a soundcard, or it could be missing a soundcard. So you could use the Optional
keyword to get around the possible NullPointerException
s.
Here's a diagram that gives you an idea of what it visually looks like to use Optional
:
So, if you're in a situation where you're pre-Java 8, here's how you'd have to deal with all the possible NullPointerException
issues:
String version = "UNKNOWN"; if(computer != null){ Soundcard soundcard = computer.getSoundcard(); if(soundcard != null){ USB usb = soundcard.getUSB(); if(usb != null){ version = usb.getVersion(); } } }
A pretty big mess right? We're probably all so used to writing this kind of code to check for null
s, but it's madness.
Java 8 Optional Syntax
So how do we write some nicer code now that we have Java 8 on our side? Let's have a look:
public class Computer { private Optional<Soundcard> soundcard; public Optional<Soundcard> getSoundcard() { ... } ... } public class Soundcard { private Optional<USB> usb; public Optional<USB> getUSB() { ... } } public class USB{ public String getVersion(){ ... } }
Pretty straight-forward I hope. All we're doing here is wrapping the Soundcard
and USB
objects with the Optional
keyword.
Fair enough, but how do we access the values? Let's use Java 8 Streams to get the job done (Note: If you haven't learned about Streams yet, check out this article I wrote on the subject).
List<String> usbVersions = computers.stream() .flatMap(Computer::getSoundcard) .flatMap(Soundcard::getUSB) .map(USB::getVersion) .orElse("UNKNOWN");
So what this code will do is it will extract all the usb versions from a list of Computer
s, but, in the case that there is no soundcard
or no usb
object available, it will fallback to the orElse()
method.
So if a computer doesn't have a Soundcard
, or if it doesn't have a USB
, we'll get “UNKNOWN” and NO NULLPOINTERS! Boo yah!
So really what this Optional
keyword is doing, is that it's forcing us to deal with the null
case.
Creating Optional Objects
Alright, so we've seen how to deal with Optional
objects that come from a stream.
But now let's take a look at how to create them.
Optional<Soundcard> sc = Optional.of(new Soundcard());
One thing to understand here is that when you perform an Optional.of()
call, the value shouldn't be null, otherwise it will still throw a NullPointerException
.
If this seems strange to you, then you will likely prefer a different method:
Optional<Soundcard> sc = Optional.ofNullable(new Soundcard());
With the Optional.ofNullable()
method, if the object being passed in is null
, you'll just get an empty Optional
object when you try to access the soundcard
inside.
Doing Stuff with your Optional Value
Does this look familiar:
SoundCard soundcard = ...; if(soundcard != null){ System.out.println(soundcard); }
Yeah, we've all been there. Let's harness the power of Optional
:
Optional<Soundcard> soundcard = ...; soundcard.ifPresent(System.out::println);
Makes a lot more sense now right? If there's a value present in the Optional
wrapper, it'll get printed out to the console. Otherwise, no worries, you're not going to throw a stupid NullPointerException
because you tried to print an empty object.
Logging just got 10 times easier and safer!
PS. you can also use a soundcard.isPresent()
check that returns true
if soundcard
contains a value.
PPS. oh yeah, you can also just get your object from the Optional
container by saying soundcard.get()
… but be careful, because that could throw a NoSuchElementException
if there is no object in the container.
Java 8 Optional – Default Values and Actions
Pre-Java 8 this was a common scenario:
Soundcard soundcard = aSoundcard != null ? aSoundcard : new Soundcard("basic_sound_card");
Let's clean that up a bit with Java 8:
Soundcard soundcard = aSoundcard.orElse(new Soundcard("default"));
So this code reads like so… I'd like to declare a soundcard
object and try to populate it with aSoundcard
. If aSoundcard
doesn't have a value, then assign a default value instead.
Now if you're new to Java 8, this might seem complicated… but it's only unfamiliar to you, it's actually pretty simple stuff.