The throwing-functions library provides copies of the functional interfaces in java.util.functions that allow their functional interface methods to throw checked exceptions.
Each of these interfaces also contains static methods unchecked and checked to convert them to and from their matching equivalents in java.util.functions. For example, to delete all files in a directory that match a filter, you can use ThrowingConsumer.unchecked:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
stream.forEach(ThrowingConsumer.unchecked(Files::delete));
} catch (UncheckedException e) {
e.throwCauseAs(IOException.class);
}The throwCauseAs method above is declared to return any type, allowing it to be used as return statement. For instance, using ThrowingToLongFunction.unchecked:
static long getTotalSize(Path dir) throws IOException {
try (Stream<Path> stream = Files.walk(dir)) {
return stream
.filter(Files::isRegularFile)
.mapToLong(ThrowingToLongFunction.unchecked(Files::size))
.reduce(0, Long::sum);
} catch (UncheckedException e) {
return e.throwCauseAs(IOException.class);
}
}Each interface has a set of default methods that allow any thrown checked exception to be handled. These come in the following variants:
onErrorThrowAsCheckedandonErrorThrowAsUncheckedtransform the caught checked exception into another exception.onErrorHandleCheckedandonErrorHandleUncheckedinvoke a function or action on the caught checked exception.onError<Operation>CheckedandonError<Operation>Uncheckeddiscard the caught checked exception and invoke an instance of the same interface or its unchecked variant.onErrorGetCheckedandonErrorGetUncheckeddiscard the caught checked exception and return the result of a supplier (only for interfaces that don't returnvoid).onErrorReturndiscards the caught checked exception and returns a fixed value (only for interfaces that don't returnvoid).onErrorDiscarddiscards the caught checked exception without doing anything else (only for interfaces that returnvoid).uncheckedwraps the caught checked exception in an instance of UncheckedException. This is similar to calling the staticuncheckedmethod with the instance as argument.
The variants ending with Checked return an instance of the same interface but with a possibly different checked exception that can be thrown.
The variants ending with Unchecked return an instance of the matching unchecked variant.
Using these methods, the above example can be changed to throw an UncheckedIOException instead:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
stream.forEach(ThrowingConsumer.of(Files::delete).onErrorThrowAsUnchecked(UncheckedIOException::new));
} catch (UncheckedIOException e) {
throw e.getCause();
}It also becomes easy to log exceptions instead of letting them be relayed to the caller:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
stream.forEach(ThrowingConsumer.of(Files::delete).onErrorHandleUnchecked(e -> logger.info("Failed to delete a file", e)));
}And the getTotalSize method shown above can be changed to not fail if a file's size cannot be determined:
static long getTotalSize(Path dir) throws IOException {
try (Stream<Path> stream = Files.walk(dir)) {
return stream
.filter(Files::isRegularFile)
.mapToLong(ThrowingToLongFunction.of(Files::size).onErrorReturn(0))
.reduce(0, Long::sum);
}
}Like the functional interfaces in java.util.functions, any thrown instance of Error, RuntimeException or one of their sub classes is relayed to the caller.