Abstract: Java 8 introduced the java.util.Optional class, based on the famous Guava class by the same name. It was said that we should hardly ever call get(). In this newsletter we offer a short tutorial that demonstrates coding examples of alternatives to get().
Welcome to the 238th edition of The Java(tm) Specialists' Newsletter, written en route back from JAX Finance in London and completed on another flight from Greece to Spain. I'm in Malaga for a few days and will speak at the Malaga Java User Group tomorrow evening the 10th May 2016. I like speaking at JUGs whilst traveling, since I meet people who are truly passionate about Java and learning.
A few weeks ago, Sanjeev Kumar made the suggestion that since he really liked my instructional videos on Vimeo, I should try to record a short message per newsletter. I liked the idea and have started doing it. I hope you find it interesting. Please let me know if you do and would like to see more videos.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
I focus my research mostly on core Java SE. Even though I
look at many other frameworks like Spring, Guava,
Eclipse Collections, JEE, they are not stored in my brain's
long-term memory. Unfortunately I had not spent a lot of time
with Optional from Google Guava, so when java.util.Optional
arrived in Java 8, it was fairly new to me. The point of
Optional
and its cousins OptionalInt
, OptionalDouble
and
OptionalLong
, is to avoid returning null
from methods. A
noble goal.
We should avoid calling get()
and instead use some other
methods that I will show in a moment. If we have to use
get()
, we should first check if the value is present.
A get()
not preceded by a call to isPresent()
is
a bug. Since we have alternatives to get()
for
most use cases, Stuart Marks recently
suggested get()
should be deprecated and replaced with the
method getWhenPresent()
. I think only Brian Goetz agreed
with him! Hopefully it won't happen, since I don't think
getWhenPresent()
is that well named either. What if it's not
present? What then? Does it block? Oh, it throws a
NoSuchElementException! So why not call it
getWhenPresentOrElseThrowNoSuchElementExceptionIfYouCallIt()
? A change
like that in Java is cumbersome. First off, they need to add
the new method and deprecate the old get()
method. They then
have to wait at least a release to delete get()
altogether.
In the meantime, we have to change all our code that
innocently calls get()
after checking that it exists with
isPresent()
to either use the new name or to instead annotate
our code with @SuppressWarnings("deprecated")
. Or we could
just ignore deprecation warnings, like we have done for the
past 20 years ...
Stuart Marks sent a very interesting post, where he shows
examples in the JDK where Optional.get()
was used and where
it could have been replaced with another mechanism. He very
kindly agreed to let me publish his findings in this
newsletter. I am hoping that it will be a "tutorial by
example" for those who have not used Optional before and
would like to learn how it works.
The first method we should learn is
Optional.ifPresent(Consumer<? super T> action)
. Not isPresent()
, but
ifPresent()
. I must admit that when I saw his reference to
this method I scratched my head and wanted to see if it
really existed. I had seen isPresent()
before, but somehow
didn't see that we also had another method that looked almost
the same. After all, "s" and "f" are just separated by a "d"
:-) Thus if you are tempted to write code like this:
if (source.isPresent()) { doSomethingWith(source.get()); }
As long as doSomethingWith()
does not throw any checked
exceptions, you could easily transform the code to:
source.ifPresent(s -> doSomethingWith(s));
Or if like me you aren't scared of method references, you could do this:
source.ifPresent(this::doSomethingWith);
Stuart dug around in the JDK and came up with a bunch of examples where this could have been used. Remember, these are JDK developers, so hopefully not complete beginner programmers. The first one is from the DependencyFinder.java:
206 if (source.isPresent()) { 207 executor.runTask(source.get(), deque); 208 }
This could be rewritten as:
source.ifPresent(archive -> executor.runTask(archive, deque));
Our next example is from JdepsTask.java
476 Optional<String> req = options.requires.stream() 477 .filter(mn -> !modules.containsKey(mn)) 478 .findFirst(); 479 if (req.isPresent()) { 480 throw new BadArgs("err.module.not.found", req.get()); 481 }
could be rewritten as
options.requires.stream() .filter(mn -> !modules.containsKey(mn)) .findFirst() .ifPresent(s -> throw new BadArgs("err.module.not.found", s));
Next we have a code snippet where the programmer did not
check that the Optional contained a value using isPresent()
.
If for some reason the subList()
was empty, the reduce()
would return an empty Optional and thus get()
would throw a
NoSuchElementException
. IDEs should pick this up and warn
us. Furthermore, we have a shorter and probably more
efficient way of joining the Strings together using
String.join()
. The code this time is from JShellTool.java
1203 String hist = replayableHistory 1204 .subList(first + 1, replayableHistory.size()) 1205 .stream() 1206 .reduce( (a, b) -> a + RECORD_SEPARATOR + b) 1207 .get();
could be rewritten as
String hist = String.join(RECORD_SEPARATOR, replayableHistory.subList(first+1, replayableHistory.size()));
Another little code snipped from Resolver.java
100 if (mref.location().isPresent()) 101 trace(" (%s)", mref.location().get());
could be rewritten as
mref.location().ifPresent(loc -> trace(" (%s)", loc);
The next one is a bit different. Here they check for whether
a particular value is not present. Instead of doing
it this way, we can first filter and confirm that if there is
a value, that it conforms to our requirements. If it
doesn't, or there never was a value, we throw the exception
using Optional.orElseThrow()
. This example is from Java 9's
new Layer class in the Java Reflection package: Layer.java:
364 Optional<Configuration> oparent = cf.parent(); 365 if (!oparent.isPresent() || oparent.get() != this.configuration()) { 366 throw new IllegalArgumentException( 367 "Parent of configuration != configuration of this Layer");
could be rewritten as
cf.parent() .filter(cfg -> cfg == this.configuration()) .orElseThrow(() -> new IllegalArgumentException( "Parent of configuration != configuration of this Layer"));
I found these examples very interesting and helpful to
understand how Optional
should be used. However, what if our
idiom is slightly different, for example something like:
Optional<BigInteger> prime = findPrime(); if (prime.isPresent()) { System.out.println("Prime is " + prime.get()); } else { System.out.println("Prime not found"); }
I posed this question to Stuart Marks and he sent me back a
neat solution involving map()
and orElse()
. In our example,
map transforms the BigInteger
to String
. However, if the
Optional
is empty, then it will simply return an empty
Optional<String>
. We can then further return a default
value if it is empty ("Prime not found") or we could throw an
exception. Here is the code:
System.out.println( findPrime() .map(p -> "Prime is " + p) .orElse("Prime not found"));
I made a suggestion to Stuart that perhaps we should also add
Optional.ifPresentElse(Consumer,Runnable)
.
Turns out this is coming in Java 9, together with some other
new methods for turning the Optional
into a composite:
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction); public Optional<T> or(Supplier<Optional<T>> supplier); public Stream<T> stream();
I would strongly encourage using Optional
in your code base,
as it reduces the possibility of a very common issue:
NullPointerException
. And if you are tempted to
use get()
, use the tips in this newsletter to follow the
best practices instead.
Kind regards
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.