A fellow developer today asked me a question about the Optional interface in Java 8. My team is still working on a Java 6 stack, but his team is blazing the trail to Java 8. I’ve used Optional a little bit in some side work, and I am a little more familiar with Guava’s version.

His use case centered around the correct syntax to rewrite this using map() instead of an if/else for something along the lines of this:

// Pseudocode
public ReturnType <T> buildThing(Optional <T> something) {
  if (something.isPresent()) {
    ParameterThing pt = new ParameterThing();
    RealReturnType <T> rt = new RealReturnType <T>(pt);//RealReturnType extends ReturnType
    return pt;
  } else {
    return new DummyReturnType <T>();//DummyReturnType etends ReturnType
  }
}

I had not dealt with the specifics of Optional yet, so here I am at 11:00 figuring it out. Here is a real working example:

public class OptionTests {
  public static void main(String[] args) {
      Optional< String> o = Arrays.asList(args).stream().findFirst();
      Integer i = o
                .filter(s -> s.matches("\\d+"))
                .map(s -> (Integer) Integer.parseInt(s))
                .orElseGet(() -> (Integer) new Random().nextInt());
      System.out.println(i);
  }
}

If args[0] appears to be an integer, it will be printed. Otherwise, it will print a random number. How does this work? Let’s break it down:

  1. An Optional is constructed from the first argument. If no arguments are present, the Optional will exist (be non-null), but will answer false to isPresent().
  2. Filter out any string that appears to be numeric. If the string is not numeric, OR if it is not present, filter will return an empty Optional.
  3. Map (convert) the string to an integer. If the Optional is empty, a new empty optional is returned.
  4. Return the value of the optional if non-empty, otherwise call the supplier. In this case, the supplier is a lambda expression that generates a new random integer. If we wanted to return a constant, this could be written as:
orElse(1);

Looking at it another way, here are the different Optionals that are created, separated out into individual variables instead of a one-line continuation:

Optional< String> o1 = Arrays.asList(args).stream().findFirst()
Optional< String> o2 = o1.filter(s -> s.matches("\\d+"));
Optional< Integer> o3 = o2.map(s -> (Integer) Integer.parseInt(s));
Integer i = o3.orElseGet(() -> (Integer) new Random().nextInt());
System.out.println(i);