Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"switch" equivalent for exception handling

This is not a question about exception handling in general, but it applies specifically end exclusively to the use of some frameworks. A few examples of typical starting points:

  • GWT: public void onFailure(Throwable caught) implementation of the AsyncCallback interface.
  • JAX-RS: public Response toResponse(E throwable) implementation of the ExceptionMapper<E extends Throwable> interface.

Both the above methods receive an instance of Throwable. Normally, I've seen developers use a simple "if/else if" block to differentiate the handling logic:

// As specified by the AsyncCallback class of the GWT framework
public void onFailure(Throwable caught) {
    if (caught instanceof AnException) {
        // handle AnException
    } else if (caught instanceof AnotherException) {
        // handle AnotherException
    } else if (caught instanceof YetAnotherException) {
        // handle YetAnotherException
    } else if (caught instanceof ...) {
        // and so on...
    }
}

Since I am not a fan of "if/else if" blocks for many reasons, I came up with the following "pattern" which converts the "if/else if" block into a "try/catch" block, behaving as if it were a "switch" block:

public void onFailure(Throwable caught) {
    try {
        throw caught;
    } catch(AnException e1) {
        // handle AnException
    } catch(AnotherException e2) {
        // handle AnotherException
    } catch(YetAnotherException e3) {
        // handle YetAnotherException
    } catch(...) {
        // and so on...
    }
}

My question is: Are there any drawbacks - in terms of performance, best practices, code readability, general safety, or just anything else I'm not considering or noticing - using this approach?

like image 853
Louis Bono Avatar asked Sep 18 '17 13:09

Louis Bono


1 Answers

Using exceptions to direct program flow under normal circumstances is a code smell, but that's not really what you're doing here. I think you can get away with this for a few reasons:

  1. We already catch and re-throw exceptions for all manner of reasons (e.g., "catch, take some action, propagate"). This is a bit different in intent, but it's no worse in terms of cost.

  2. You've already incurred the cost of this exception being thrown at least once. You've possibly incurred the cost of its causes being thrown, caught, and wrapped or re-thrown. The cost of filling in the stack traces has already been paid. Re-throwing an already-populated exception one more time is not going to increase the order of complexity.

  3. You are not using exceptions to direct the flow of the normal code path. You're reacting to a error, so you are already on the exceptional path, and you should rarely (if ever) end up here. If this pattern is inefficient, it will hardly matter unless you are encountering lots of exceptions, in which case you have bigger problems. Spend your time optimizing the paths you expect to take, not the ones you don't.

  4. Aesthetically, there are few things that make my skin crawl like long chains of if/else if blocks, especially when the conditions are merely type-checking. What you're proposing is, in my opinion, far more readable. Having multiple, ordered catch clauses is common, so the structure is mostly familiar. The try { throw e; } preamble may be unorthodox, but it's easy enough to reason about.

Just be wary when propagating Throwable. Some errors, like the VirtualMachineError hierarchy, are a sign that something has gone horribly wrong, and they should be allowed to run their course. Others, like InterruptedException, communicate something about the state of the originating thread, and they should not be blindly propagated on a different thread. Some, like ThreadDeath, span both categories.

like image 50
Mike Strobel Avatar answered Sep 20 '22 11:09

Mike Strobel