Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rethrowing checked exceptions

public void foo() {
 begin();
 try {
  ...
  commit();
 } catch (Exception e) {
  rollback();
  throw e;
 }
}

In the sample above, there is an error because foo has no throws Exception. Adding that wouldn't make do the method's usability a lot of good either.

What's the best way to do this? How do you do something if an error occurs without really "handling" the error?

like image 423
Bart van Heukelom Avatar asked Dec 02 '22 03:12

Bart van Heukelom


2 Answers

Since Java 8 we use

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T> the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

You can rethrow a checked exception, but only by avoiding the compilers checked exception validation.

public void foo() throws MyCheckedException {
 begin();
 try {
  ...
  commit();
 } catch (Exception e) {
  rollback();
  // same as throwing an exception without the compiler knowing.
  Thread.currentThread().stop(e); 
 }
}

Before you use stop() you should read http://download.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

Thread.currentThread().stop(e) .. is behaviorally identical to Java's throw operation, but circumvents the compiler's attempts to guarantee that the calling method has declared all of the checked exceptions that it may throw:

like image 109
Peter Lawrey Avatar answered Dec 17 '22 04:12

Peter Lawrey


At least two approaches come to mind, which are usually going to be combined depending on what you want foo to do:

1. Catch and rethrow only the relevant exceptions

There are only so many exceptions the code in your main flow can throw (probably mostly SqlExceptions). So only catch and rethrow those, and declare that you're doing so. More to the point, rethrow only the ones you're not actually handling (in your simplified sample code, you're not handling any, but your real life code is probably more subtle).

Mind you, some of the exceptions may be runtime exceptions, and so you may want to combine this with the below.

2. Don't catch the exception at all

Like this:

// Signature changes to include any exceptions that really can be thrown
public void foo() throws XYZException, ABCException {
 // A flag indicating that the commit succeeded
 boolean done = false;

 begin();
 try {
  // Don't have any `return` statements in here (or if you do,
  // set `done` to `true` first)

  ...
  commit();
  done = true; // Commit didn't throw an exception, we're done
 } finally {
  // finally clause always happens regardless
  if (!done) {
    // We must be processing an exception; rollback
    try {
      rollback();
    } catch (Exception e) {
      // quash it (e.g., leave this block empty), we don't want
      // to mask the real exception by throwing a different one
    }
  }
 }
}

Naturally your signature needs to include any exceptions that may be thrown in the main flow, but that's what you're trying to do, if I'm understanding you correctly.

Again, you may well combine these two approaches, because you may want to handle some exceptions and not others.

like image 32
T.J. Crowder Avatar answered Dec 17 '22 02:12

T.J. Crowder