From Effective Java (Joshua Bloch)
- Only use Exceptions for exceptional conditions (never use for ordinary control flow)
- Use checked exceptions for recoverable conditions and runtime exceptions for programming errors.
- Avoid unnecessary use of checked exceptions
Avoid checked exceptions.
http://www.mindview.net/Etc/Discussions/CheckedExceptions
http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html
- Checked exceptions are often considered a flawed feature by many. It is not a bad idea but people don't really understand how to use them.
- Java frameworks like Spring, Hibernate... they all mostly throw unchecked exceptions.
- C# did not implement checked exceptions on purpose. Scala neither.
- It's not because it's unchecked that you can't catch it.
- It is not because it's unchecked that you can't translate it (wrap it)
- It's not because it's unchecked that it's not part of the contract. Indeed you can declare the method to throw unchecked exceptions.
- Checked exceptions increase coupling between the client and the library
One very important thing to understand is that any piece of code can produce an exception.
It is that it is not because a method declares throwing an IOException that it may not throw any other exception. It can throw any other runtime exception (generic or custom). With checked exceptions, developers tend to think the opposite, and think that catching IOException will handle all the exceptional cases, but it is not true!
A compile time feature only
Only the compiler will tell you when you forget to catch or rethrow a checked exception. At runtime, there is no difference.
This means that by using type erasure tricks, you can throw a checked exception without even needing it to be part of the method contract.
You can find examples of this trick as being called SneakyTrow here:
https://stackoverflow.com/a/4890489/82609
Lombok also provides a @SneakyThrow annotation to put on methods so that you don't need to declare checked exceptions in the method signature.
Only use checked exceptions when your class client can probably recover from the exception.
This is the Sun recommendation.
Basically, an attempt to connect to the database will throw a checked exception, and the retry strategy code will catch these checked exceptions on connection attemps. When the number of retry is exceeded, the retry strategy will throw an unchecked exception, meaning it is not a recoverable exception since the recovery strategy has already been tried.
You can use Spring RetryTemplate for that by the way.
Avoid exception codes
The exception type should be sufficient for flow control decisions. Parsing exceptions or flow control will only create useless code. Add more exception types, as much as you have exception codes.
Fail fast
Let all the non recoverable exceptions being throw up to the IHM layer.
If you use a framework that declare checked exceptions and you have no recovery strategy for them, do not hesitate to wrap them into unchecked exceptions.
If you can't recover, then you should not do "catch and log". Or worse, you should not do "catch and return null". This will produce inconsistent software and you will probably have another exception raised a bit later in your program, but you won't be able to understand why. Returning null will only create a NullPointerException later.
The IHM layer technology may have an exception handler/mapper.
Web IHM layers have exception mappers so that you can say "this exception produces a 404 error".
The functional approach
For information: in functional languages it is often considered a bad practice to use exceptions for flow control.
Instead of throwing exceptions, we usually return "enhanced types", like Either[Error,MyResultType].
The returned instance is either an error or a success, the success being a MyResultType instance returned.
Exceptions are not performant for flow control
Creating an exception has a cost (creating the stacktrace). It costs much more than a normal flow using if, else...
Do not use them for flow control if you can avoid them.
Basically, you can mostly always avoid them, but in Java it may be sometimes more convenient to use them for flow control in some situations. In functional languages the Either monad helps again.
Use assertions
If something on your program is assumed to be true by the developer, use assertions so that you guarantee your assertion is true.
Take a look at Guava Preconditions:
https://code.google.com/p/guava-libraries/wiki/PreconditionsExplained
Or you can use Java native assertions, or some custom code.
This helps to fail fast.