Java Reference
In-Depth Information
* @param exceptionHandler The handler for exceptions; never {@code null}j
* @param exceptionClasses The classes being handled by this handler;
* never {@code null}
* @return {@code this} for chaining
*/
public <EXCEPTION_T extends Exception> Result<RESULT_T> whenException(
Consumer<? super EXCEPTION_T> exceptionHandler,
Class<? extends EXCEPTION_T>... exceptionClasses
) {
Objects.requireNonNull(exceptionHandler, "exception handler");
Objects.requireNonNull(exceptionClasses, "exception classes to handle");
exception.ifPresent(e -> {
Stream<Class<? extends EXCEPTION_T>> classesStream =
Stream.of(exceptionClasses);
Optional<Class<? extends EXCEPTION_T>> match = classesStream.filter(
c -> c.isInstance(e)
).findAny();
match.ifPresent(exceptionClass -> {
EXCEPTION_T ex = exceptionClass.cast(e);
exceptionHandler.accept(ex);
}
);
}
);
return this;
}
}
The first things to note are the constructors and the accessors: the class encapsulates two
Optional
instances: one representing the result, and one representing the exception that was thrown. A given result
can either be a result or an exception that was thrown, so one of these will be the empty optional and the
other one will be populated.
The
hasResult()
method shows a common theme. It simply delegates to the
result.isPresent()
method, and this class will regularly end up simply delegating to the appropriate
Optional
instance method.
In this case, we check if we have a result by checking if the result is present.
The
getOrDie()
method provides a convenient way for us to get at the result if we really know that the
result is present. If the caller is wrong and the result is not present, it will throw a
NoSuchElementException
with the resulting exception as a suppressed exception. This is implemented via the
orElseThrown
method, which either returns the result or throws the exception provided by a
Supplier
. Our
Supplier
implementation will put the exception into the stack trace of the
NoSuchElementException
, which should
help debugging. (The
“INTERNAL ERROR”
exception is only ever reached if both the exception and the result
were initially populated as
null
, which should simply never happen.)
The
map
function will transform the result, if it exists; otherwise, it will construct a new result and pass
the exception along. This creates a way to act upon the result without losing the exception information. In
most case when you reach for
getOrDie()
, you really want
map()
.
The
ifPresent
and
ifNotPresent
methods provide a convenient way to consume the result or the
exception if they are present. This, along with
map()
, allows the user to specify an implementation without
having to worry about the state of the result, which makes for a very readable API. Note that the big
difference with
ifPresent()
/
ifNotPresent()
and
map()
is the return value:
ifPresent()
/
ifNotPresent()
loses information but converts to the more common Optional type, while
map()
retains information but
stays with the present type.