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:

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.