Exceptional Exception Handling

This is another post in our Code Health series. A version of this post originally appeared in Google bathrooms worldwide as a Google Testing on the Toilet episode. You can download a printer-friendly version to display in your office.

by Yiming Sun

Have you ever seen huge exception-handling blocks? Here is an example. Let's assume we are calling bakePizza() to bake a pizza, and it can be overbaked, throwing a PizzaOverbakedException.

class PizzaOverbakedException extends Exception {};


void bakePizza () throws PizzaOverbakedException {};


try {

  // 100+ lines of code to prepare pizza ingredients.

  ...

  bakePizza();

  // Another 100+ lines of code to deliver pizza to a customer.

  ...

} catch (Exception e) {

  throw new IllegalStateException(); // Root cause ignored while throwing new exception.

}

Here are the problems with the above code:

  • Obscuring the logic. The method bakePizza(), is obscured by the additional lines of code of preparation and delivery, so unintended exceptions from preparation and delivery may be caught.
  • Catching the general exception. catch (Exception e) will catch everything, despite that we might only want to handle PizzaOverbakedException here.
  • Rethrowing a general exception, with the original exception ignored. This means that the root cause is lost - we don't know what exactly goes wrong with pizza baking while debugging.

Here is a better alternative, rewritten to avoid the problems above.

class PizzaOverbakedException extends Exception {};


void bakePizza () throws PizzaOverbakedException {};


// 100+ lines of code to prepare pizza ingredients.

...

try {

  bakePizza();

} catch (PizzaOverbakedException e) {  // Other exceptions won’t be caught.

  // Rethrow a more meaningful exception; so that we know pizza is overbaked.

  throw new IllegalStateException(“You burned the pizza!”, e);  

}

// Another 100+ lines of code to deliver pizza to a customer.

...