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);
