When a checked exception is thrown, there are several typical ways to deal with it.
These methods are the most appropriate ways to deal with a checked exception:
1. Handle it. Do something in response to the exception other than just re-throwing it or logging. If you have an I/O exception, maybe retry the operation.
2. Rethrow it. If you can't handle it, rethrow it, and maybe someone else above you in the call stack can handle it.
3. Wrap it as a runtime exception and rethrow it.
#2 and #3 become a bit more painstaking when methods throw multiple checked exceptions since Java doesn't support multiple exception catch blocks.
There are other ways to deal with it. These are not recommended, but any developer has seen these scenarios:
4. Log it/print stack trace and swallow the exception
5. Do nothing at all and swallow the exception
It's obvious that #4 and #5 can have very bad effects. These exceptions could be indicating some serious problems. While #4 will result in an entry in the log, it still could leave the application in a state that results in some unpredictable behavior. #5 should never be done.
With unchecked exceptions, you have all of the same options. However, #4 and #5 are much less likely since developers frequently do these out of laziness or because their IDE provides this as the default behavior.
Those that favor checked exceptions will say that you are much less likely to handle an unchecked exception since you the compiler does not force a method to declare that it is thrown. While this is true, the same can happen if the method wraps the checked exception in an unchecked one. And, in all actuality, how frequently do you actually handle exceptions vs logging and rethrowing them. If you're like me, you spend much more time doing the latter.
To be fair, there are some times when you really do want to handle the exception. Take the case of the IO exception when you want to retry an operation. Even if it was an unchecked exception, you could use annotations on the method you want to retry (not necessarily the direct method that throws the IO exception), which will work for an unchecked (or checked version) of the IO exception.
@RetryOnException(type=IOException.class)
public void saveState() {
State state = getState();
writeStateToFile(state);
}
private void writeStateToFile(State state) {
// write the state to file
}
The @RetryOnException idea puts the responsibility on the method that needs to be retried but doesn't require everyone in the call stack to declare an IOException. This idea will work the same for checked or unchecked exceptions.
The idea of using annotations to handle exceptions is a clever idea. I think this could be taken to another level with an aspect-oriented way to wire exception handling to the exception-throwing code. For instance, if you want to implement a logging message followed by a notification message every time an IOException were to occur, you could define the method in one place and wire the action using the annotations.
ReplyDeleteI think AOP definitely has a place in exception handling. In the example you gave, it definitely would cut down on try/catch and log exception blocks. But it gets a little messy since you'd need to wire the AOP to handle on catching of the exception. What if someone catches it and rethrows it, and then someone else catches it. You could end up logging it multiple times. If you didn't have to catch any exceptions, you could then just log anytime you have an uncaught exception. You can do a lot of that with the DefaultUncaughtExceptionHandler, but I think that AOP provides more power.
ReplyDelete