Loading... (original) (raw)
There are many complicated try/catch/throw sequences which sort and sift unchecked, checked-and-expected, and unexpected exceptions. These occur near calls to the CoreReflection API and MethodHandle invocations, as well as other places which are generic across exceptions.
The following methods are likely to help write such logic more safely and concisely.
The exact design of these methods should be adjusted by carefully studying existing use cases for exception forwarding, wrapping, and unwrapping, especially those in try/catch/throw constructs near calls to MethodHandle.invoke and Method.invoke.
It is possible to create methods that resurface exception causes and suppressed exceptions; see below. It is not necessarily a good idea to do this, but examples are shown below for completeness.
/**
* If this exception is unchecked throws it,
* else returns this exception normally.
* Equivalent to {@code throwIf(Error.class).throwIf(RuntimeException.class)}.
*
* @throws this exception, if it is an unchecked exception
* @return this exception, if it is a checked exception
* @since 1.9
*/
public final Throwable throwIfUnchecked() {
return throwIf(Error.class).throwIf(RuntimeException.class);
}
/**
* If this exception is of the specified type, throws it,
* else returns this exception normally.
*
*
For example, if an exception is not statically checkable,
* but is expected to be either unchecked or an {@code IOException},
* it can be thrown or disposed of as an assertion error like this:
*
* void assertUncheckedOrIOException(Throwable t) throws IOException {
* t.throwIfUnchecked().throwIf(IOException.class);
* throw new AssertionError(t);
* }
*
* @param exClass the class of exception to be thrown
* @throws this exception, if it is the expected class
* @return this exception, if it is some other class
* @since 1.9
*/
public final Throwable throwIf(Class exClass) throws X {
if (exClass.isInstance(this)) throw exClass.cast(this);
return this;
}
/**
* If this exception has a suppressed exception of the specified type, throws that,
* else returns this exception normally.
*
*
* @param exClass the class of exception to be thrown
* @throws the first suppressed exception of the expected class
* @return this exception, if there is no such suppressed exception
* @since 1.9
*/
public final Throwable throwSuppressedIf(Class exClass) throws X {
for (Throwable supp : suppressedExceptions) {
if (exClass.isInstance(supp)) throw exClass.cast(supp);
}
return this;
}/**
* If this exception has a cause (directly or recursively) of the specified type, throws that,
* else returns this exception normally.
*
*
* @param exClass the class of exception to be thrown
* @throws the innermost cause of the expected class
* @return this exception, if there is no such cause
* @since 1.9
*/
public final Throwable throwCauseIf(Class exClass) throws X {
Throwable cause = this.cause;
if (cause == null || cause == this) return this;
return cause.throwCauseIf(exClass).throwIf(exClass);
}