Java 8 Optional : How To ?

Java Logo
1 - The Optional Type :



An Optional<T> object is either a wrapper for an object of type T or for no object. It is intended as a safer alternative than a reference of type T that refers to an object or null. But it is only safer if you use it right.
The get method gets the wrapped element if it exists, or throws a NoSuchElementException if it doesn’t. Therefore,


Optional<T> optionalValue = ...;optionalValue.get().someMethod()


is no safer than
     T value = ...;     value.someMethod();
As you saw in the preceding section, the isPresent method reports whether an Optional<T> object has a value. But
     if (optionalValue.isPresent()) optionalValue.get().someMethod();
is no easier than
     if (value != null) value.someMethod();
In the next section, you will see how you should really work with Optional values.


2 - Working with Optional Values :

The key to using Optional effectively is to use a method that either consumes the correct value or produces an alternative. There is a second form of the ifPresent method that accepts a function. If the optional value exists, it is passed to that function. Otherwise, nothing happens. Instead of using an if statement, you call
  optionalValue.ifPresent(v -> Process v);


For example, if you want to add the value to a set if it is present, call
  optionalValue.ifPresent(v -> results.add(v));
or simply
  optionalValue.ifPresent(results::add);


When calling this version of ifPresent, no value is returned. If you want to process the result, use map instead:
  Optional<Boolean> added = optionalValue.map(results::add);
Now added has one of three values: true or false wrapped into an Optional, if optionalValue was present, or an empty optional otherwise.


You have just seen how to gracefully consume an optional value when it is present. The other strategy for working with optional values is to produce an alternative if no value is present. Often, there is a default that you want to use when there was no match, perhaps the empty string:
  String result = optionalString.orElse("");
// The wrapped string, or "" if none


You can also invoke code to compute the default, 

  String result = optionalString.orElseGet(() -> System.getProperty("user.dir"));
// The function is only called when needed


Or, if you want to throw another exception if there is no value,
  String result = optionalString.orElseThrow(NoSuchElementException::new);
// Supply a method that yields an exception object


3 - Creating Optional Values
So far, we have discussed how to consume an Optional object that someone else created. If you write a method that creates an Optional object, there are several static methods for that purpose. Either create an Optional.of(result) or Optional.empty()
For example,

     public static Optional<Double> inverse(Double x) {
        return x == 0 ? Optional.empty() : Optional.of(1 / x);
     }


The ofNullable method is intended as a bridge from the use of null values to optional values. Optional.ofNullable(obj) returns Optional.of(obj) if obj is not null, and Optional.empty() otherwise.


4 - Composing Optional Value Functions with flatMap
Suppose you have a method f yielding an Optional<T>, and the target type T has a method g yielding an Optional<U>. If they were normal methods, you could compose them by calling s.f().g(). But that composition doesn’t work here, since s.f() has type Optional<T>,has not T. Instead, call 
       Optional<U> = s.f().flatMap(T::g);
If s.f() is present, then g is applied to it. Otherwise, an empty Optional<U> is returned.
Clearly, you can repeat that process if you have more methods or lambdas that yield
Optional values. You can then build a pipeline of steps that succeeds only when all parts do, simply by chaining calls to flatMap.
For example, consider the safe
inverse method of the preceding section. Suppose we also have a safe square root:
   public static Optional<Double> squareRoot(Double x) {      return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));   }
Then you can compute the square root of the inverse as
     Double result = inverse(x).flatMap(MyMath::squareRoot);
or, if you prefer,
     Double result = Optional.of(-4.0).flatMap(Test::inverse).flatMap(Test::squareRoot);
If either the inverse method or the squareRoot returns Optional.empty(), the result is empty.


Comments