NoException - Exception handlers for Java
NoException is functional programming for Java exception handlers. Many applications contain thousands of try-catch constructs and it's a mess. Catch clauses are verbose, repetitive, inconsistent, buggy, and hard to test. NoException provides a set of predefined exception handlers (try-catch replacements) that are concise and neat. You can define your own reusable handlers to fit policies of your project.
Example:
System.out.println(
Exceptions.log().get(() -> "test".substring(5)).orElse("fallback"));
The above is equivalent to the following try-catch code:
Logger logger = LoggerFactory.getLogger(...);
String result;
try {
result = "test".substring(5);
} catch (Throwable exception) {
logger.error("Caught exception", exception);
result = "fallback";
}
System.out.println(result);
There's more to NoException. Read on.
Download
Get NoException from Maven Central:
<dependency>
<groupId>com.machinezoo.noexception</groupId>
<artifactId>noexception</artifactId>
<version>1.6.2</version>
</dependency>
Or clone sources from GitHub or Bitbucket. Don't forget to configure your build for Java 8+. Sources and binaries are distributed under Apache License 2.0.
Why?
Application code is often littered with boilerplate try-catch blocks. Verbosity of try-catch encourages developers to avoid it and to propagate exceptions instead. Notable recurring examples include fallbacks, callbacks, event/job loops, executors, and lambdas.
// Fallbacks: Exception severity escalated where log & fallback would suffice.
important.add(unimportant());
// Callbacks: Exception erroneously propagated to unrelated code.
if (callback != null)
callback.run();
// Event/job loops: Exception kills the whole loop including unrelated jobs.
for (Job job : jobs)
job.run();
// Executors: Exception lost in Future that is not awaited.
executor.submit(() -> Files.write("file", data));
// Lambdas: Compiler error due to checked exception.
process(() -> Files.readAllLines(at));
Tutorial
NoException defines a set of reusable checked and unchecked exception handlers (or exception blocks) and lets you easily define your own. NoException's handlers are concise, lambda-friendly, and thoroughly tested. They can be used to ensure correct and consistent exception handling throughout your project.
Catch-all handlers
If you just want to get rid of all exceptions, use
Exceptions.log()
.
String test = null;
Exceptions.log().run(() -> System.out.println("This is a " + test));
If exception is thrown, Exceptions.log()
will catch it,
log it via SLF4J, and then it will return normally.
Fallbacks
To return a value, call get(Supplier)
instead of run(Runnable)
.
It returns standard Optional
,
which lets you specify fallback value via orElse(T)
or fallback supplier via
orElseGet(Supplier)
.
System.out.println(
Exceptions.log().get(() -> "test".substring(5)).orElse("fallback"));
Pass-through handlers
If you prefer the handler to rethrow the exception after processing it,
you can call passing()
method on the handler
to get a pass-through version of the same exception handler.
for (String item : List.of("three", "two", "one")) {
System.out.println(
Exceptions.log("Failed: " + item).passing().get(() -> item.substring(4))));
}
Notice there's no need for fallback value here.
Checked exceptions
You can avoid checked exception nuisance, especially on lambdas, by either wrapping checked exceptions in unchecked ones using
Exceptions.wrap()
or by sneaking them past compiler checks using
Exceptions.sneak()
,
which will run some perfectly legal java code that tricks the compiler to let an undeclared checked exception through.
byte[] utf = Exceptions.sneak().get(() -> "test".getBytes("UTF-8"));
The above code can still throw checked exceptions, but the compiler believes it will not.
If you want to also catch the checked exceptions, combine checked and unchecked exception handlers:
static final List<String> lines = Exceptions.log()
.get(Exceptions.sneak().supplier(() -> Files.readAllLines("test.txt")))
.orElse(Collections.emptyList());
Functional interfaces
To handle exceptions from functional interfaces with parameters, first wrap the functional interface and then apply its parameters.
Function<String, String> hello = x -> "Hello " + x;
System.out.println(Exceptions.log().function(hello).apply(null).orElse("Who?"));
Method function(Function)
above returns OptionalFunction
,
a special functional interface defined by NoException that returns Optional
when executed.
In order to pass it to a method that expects standard Function
,
you can call orElse(T)
method on the OptionalFunction
.
Function<Object, String> format = x -> "[" + x.toString() + "]";
Function<Object, String> safe = Exceptions.log().function(format).orElse("???");
System.out.println(safe.apply(null));
Predefined handlers
Class Exceptions
provides several predefined exception handlers:
log()
- Logs the exception to SLF4J and swallows it.log(Logger)
- Like above, but with custom logger.log(Logger, String)
- Like above, but with custom message.log(Logger, Supplier<String>)
- Like above, but with lazily evaluated message.silence()
- Silently swallow the exception.sneak()
- Smuggle checked exceptions past compiler checks.wrap()
- Wrap checked exceptions in unchecked ones.wrap(Function)
- Like above, but with custom wrapper.ignore()
- Let the exception through (a no-op).
Custom handlers
To create custom exception handler, override
handle(Throwable)
method of
ExceptionHandler
.
public class ExceptionCounter extends ExceptionHandler {
private long count;
@Override public synchronized boolean handle(Throwable exception) {
++count;
return true;
}
public synchronized long report() {
return count;
}
}
public class ExceptionPolicy {
private static final ExceptionCounter counter = new ExceptionCounter();
public static ExceptionCounter count() {
return counter;
}
}
And use it anywhere:
ExceptionPolicy.count().run(() -> oftenFailingCode()); System.out.println(ExceptionPolicy.count().report());
A pass-through version can be derived by calling
ExceptionHandler.passing()
or you can directly implement ExceptionFilter
.
You can also define custom checked exception policy by implementing
CheckedExceptionHandler
,
but you are usually better off supplying custom wrapper to
Exceptions.wrap(Function)
.
public class ExceptionPolicy {
public static CheckedExceptionHandler wrap(String message) {
return Exceptions.wrap(e -> new RuntimeException(message, e));
}
}
// example usage
ExceptionPolicy.wrap("Failed to save").run(() -> Files.write(path, data));
Configurator
You would normally use your IDE's auto-complete. This configurator is here only to help you understand the API. It shows canonical, idiomatic code.
String result = Exceptions.log().get(() -> /* ... */).orElse("fallback");
// multi-line example
String result = Exceptions.log().get(() -> {
// ...
return "result";
}).orElse("fallback");
See Exceptions.log()
, ExceptionHandler.get(Supplier)
.
Supported functional interfaces
NoException supports all standard functional interfaces in Java.
The following table links to javadoc for every combination of functional interface and implemented feature.
Link is missing only where it does not make sense, for example when the functional interface returns void
or it is not parameterless.
Alternatives
NoException was developed shortly after Java 8 launch, but I kept it as an internal project. By the time I got around to publishing it, there were several other projects solving the same problem. I am nevertheless pleasantly surprised that NoException is a clean superset of functionality in the other projects.
NoException | fge | jOOL | StreamUnthrower | Liebenberg | Durian | Faux Pas | |
---|---|---|---|---|---|---|---|
Logging handler | yes | no | no | no | rethrowing | yes | no |
Silent handler | yes | no | no | no | yes | yes | no |
Custom handler | yes | no | yes | no | yes | yes | no |
Wrapped rethrow | yes | yes | yes | no | no | yes | no |
Sneaky rethrow | yes | yes | yes | yes | yes | no | yes |
Wraps lambda | yes | yes | yes | no | yes | yes | yes |
Runs lambda | yes | no | no | yes | no | yes | no |
Optional return | yes | custom | no | no | no | fallback only | no |
Supported interfaces | all | all | all | custom | all | few | some |
Tutorial | yes | yes | yes | yes | yes | sort of | yes |
Javadoc | yes | no | yes | no | yes | yes | no |
Maven Central | yes | yes | yes | no | yes | yes | yes |
Contribute
NoException was developed by Robert Važan. If you have any suggestions, including requests for documentation, report an issue (GitHub, Bitbucket) or submit a pull request (GitHub, Bitbucket). You can reach the author via email .