Are there any good resources for planning how exceptions will be used from an architecture perspective? (Or provide your suggestions directly here.) In projects on which I have worked I find that a few common Exceptions are used over and over again and tend to lose their meaning. From: http://jamesjava.blogspot.com/2007/10/exception-plan.html
I try to ensure that the number of places using of try/finally greatly outnumbers the number of places using try/catch. catch will usually be reserved for catching exceptions at the top-level - where they can sensibly be handled, or dealing with exceptions from platform APIs.
In this case, as long as you give exceptions meaningful messages, and chain exceptions where necessary, not being able to identify an exception by its class isn't a big loss. I often throw RuntimeException rather than try to develop an exception class for every conceivable error case - but your opinion on that will depend on where you sit on the checked vs unchecked exception debate.
I generally find that obsessing over checked exceptions is overkill. Exceptions should be reserved for unexpected error conditions from which the program cannot reasonably recover. For these, I tend to agree with what you've observed, that special error types tend to lose their meaning.
As a general rule, throw an exception when all of the following are true:
- The condition can't be recovered from here.
- The caller can't reasonably be expected to recover from this condition.
- Somebody will want to debug the situation, so they might want to see the stack trace.
You'll find that if all of those are true, the type of the exception is not important, and you may as well throw java.lang.Error. If any of them are false, then an exception is probably overkill (do you really need the type, message, and stack trace?)
Instead, consider augmenting the return type of methods to indicate expected kinds of failure. For example, use an Option<A> type for methods where it may make sense to return no value in some cases. Use an Either<A,B> type for methods where you might need to return a value whose type depends on failure or success. Where you might have had specialised Exception subtypes before, you can instead just use Either<Fail,A> where Fail is just an enum over the kinds of failures that are expected.
I half agree with Apocalisp's comment. Instances of Exception should be reserved for cases where a data or processing error has taken place, but can be recovered from by User or System intervention. Instances of RuntimeException should be reserved for those occasions when no intervention within the confines of your application can resolve the issue. These two types are thusly known as Checked and Unchecked exceptions.
An example of Unchecked exceptions would be if a physical resource (such as a database, or a message bus) is unavailable. A RuntimeException is good in this case because you are not planning for the resource to be unavailable, so your business logic doesn't have to constantly check for DatabaseUnavailableException, or something similar. You can handle RuntimeExceptions in a different way (maybe AOP sending an Email) to report the outage and let staff or support fix the physical issue.
Examples of Checked exceptions would be poor data input, or incomplete data access or something which actually fails business logic, but can be checked and recovered from. An example of this might be that a User you searched for is unavailable. It's a separate condition in your business logic that may be checked and handled (say by the View portion of your application which then reports the Exception's message as an error message to the User).
Many frameworks use this model and it seems to work better than I've seen otherwise.
That being said, many do replace checked exceptions with returned nulls, -1, empty String, or Number.MIN_VALUE. While this is okay, if you are not routinely expecting these values from a datasource or method, it should probably represented as an exception.