When to recast exceptions
The technique of recasting an exception, typically
recasting a checked exception into an unchecked exception,
can be an extremely powerful technique.
Care should be taken not to overuse recasting when the caller really should expect
an exception to occur and be able to handle an exception cleanly. But in the right circumstances,
recasting an exception offers various advantages and in some cases is almost necessary.
Recasting from checked to checked
Occasionally, we may recast a checked exception to another type of checked exception.
We'd generally do this in cases where detecting, say, an IOException in a
particular part of our code indicates a more specific type of error,
and we want to signal that fact to the caller. For example, imagine a method
that connects to a database, runs some SQL and then closes the connection.
Both connecting to a database
and running some SQL against it can throw a SQLException. We could
catch the SQLException while connecting and recast it to a
ConnectionException (an exception class that we create).
But when running the SQL, we would leave
any SQLException intact. Thus, the caller would easily know what the source of
the problem was.
public void connectAndRunSql(String sql) throws SQLException, ConnectionException {
Connection conn;
try {
conn = createConnection();
} catch (SQLException sqlex) {
throw new ConnectionException(sqlex);
}
// in the remainder of the method, let SQLException be propagated
try {
// ... execute SQL on conn ...
} finally {
conn.close();
}
}
Recasting from checked to unchecked
Avoiding exception "fuss"
There are parts of the Java API that are either (a) just too fussy when it comes to exceptions;
or (b) an interface or abstract class where it is sensible to throw a checked exception in some cases,
but where in others the exception would in practice be massively rare. Examples include:
- invoking methods via reflection;
- deserialising objects (when deserialising a String,
we have to catch ClassNotFoundException just in case the String class
is missing...);
- calling methods declared to throw InterruptedException;
- reading/writing to ByteArrayInputStream and ByteArrayOutputStream;
- my all-time favourite, the DOM XML parsing API1.
For example, let's say we're calling some method that takes an InputStream to read some
data:
public void processData(InputStream in) throws IOException {
...
}
Because it performs I/O on an InputStream, the method throws IOException.
But if we know that in practice, we are passing it a ByteArrayInputStream, then
we don't expect the exception to ever be thrown, so might write the following:
ByteArrayInputStream in = ...
try {
processData(in);
} catch (IOException ioex) {
throw new RuntimeException("Unexpected I/O error", ioex);
}
When we're "not allowed" to throw an exception
When you override a method (or implement a method from an interface), the overrding
method cannot declare new execptions not declared by the method being overridden.
In practice, there are some obvious cases where we do need to call methods that can
throw exceptions, but don't really expect to deal with them inside our method. A common
example is Thread.run(). In such cases, we may choose to recast the exception to
a RuntimeException.
1. This is undoubtably one of the worst offenders in forcing the
caller to handle a whole raft of stupid checked exceptions at every possible moment.
(Does anyone really expect to have to handle a "ParserConfigurationException"...??)
Never design an API like this.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.