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 extends 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:
- 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().
- 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.
- Map (convert) the string to an integer. If the Optional is empty, a new empty optional is returned.
- 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);