In Java, sometimes we get into the situation where a method call forces us to handle a checked exception, but we’re not interested in it right now. E.g., an UncaughtExceptionHandler
in the thread may take care of it.
On the other hand, sometimes APIs handle all errors as RuntimeException
s. But for us, the exception handling from this API must be done immediately, for example, by an error dialog so that the exception is not just rushing through.
A nice example of the first case is a method reference call within chained stream calls, as in the following example:
|
|
The chaining of the single operations leads to a clean code if there would not be the IOException, which we have to handle for the readAllBytes
call. However, we may not be interested in this exception and would like to leave the exception handling to the caller of our method.
To solve the problem, we have to wrap the exception in a RuntimeException. But this has the consequence that the one-liner becomes a six-liner. And that for something that we didn’t want to care about.
As seen in the following example, this leads to unnecessary illegibility of the code:
|
|
Generics to the Rescue
As designers of an API, we face the dilemma of weighing which error to treat as a checked or unchecked exception.
Fortunately, Java provides us with a mechanism that allows the caller to decide how our API should behave. We may use a generic when specifying the exceptions thrown by a method:
|
|
If the caller wants to handle errors as a checked exception, he needs to create an instance that uses a checked exception as a type parameter:
|
|
On the other hand, if he doesn’t want to do that, he can pass a runtime exception as a parameter type:
|
|
However, within the API method, we need to throw the correct exception. Unfortunately, because of the type erasure, we can’t do just by instantiating the type parameter:
|
|
We need to create a helper construct for this.
Using a Dedicated Error Handler
The following helper construct is a dedicated error handler, which will handle the creation of the exception:
|
|
From this abstract class (which also could be an interface), we derive two child classes. The first one is creating a checked exception:
|
|
and the second one is creating a runtime exception:
|
|
Our API method is now getting the error handler instance as an argument and in the case of an error, obtains a new exception instance from it:
|
|
The caller can now decide how he wants our method to behave regarding exceptions in case of an error, by either passing an instance of CheckedExceptionHandler
or CheckedRuntimeExceptionHandler
.
Example for an IOException Error Handler
Since both error handler derivations do not have any instance state, we can make a small optimization here by providing two generic instances. For the handling of IOExceptions
, this could look like the following example:
|
|