Running on Java 24-ea+24-2960 (Preview)
Home of The JavaSpecialists' Newsletter

238java.util.Optional - Short Tutorial by Example

Author: Dr. Heinz M. KabutzDate: 2016-05-09Java Version: 8Category: Language
 

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.

java.util.Optional - Short Tutorial by Example

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

 

Comments

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)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...