The first example would generally be seen as the better approach.
You should also not consider the MyBusinessException
to be wrapping the NumberFormatException
, but rather the NumberFormatException
is the cause of the MyBusinessException
.
Exceptions should be appropriate for the interface being exposed. A caller of your interface should not need to know, or deal with, the implementation details. Unless a NumberFormatException
genuinely makes sense as a type of error when calling exampleOneException
, it should be translated into a more suitable exception.
A more concrete example typically includes differing implementations, where the users of the interface should not be required to deal with the implementation specifics (which may not even be known at compile time).
interface MyRepository {
Object read(int id) throws ObjectNotFoundException;
}
// a sql backed repository
class JdbcRepository implements MyRepository {
public Object read(int id) throws ObjectNotFoundException {
try { ... }
catch (SQLException ex) {
throw new ObjectNotFoundException(ex);
}
}
}
// a file backed repository
class FileRepository implements MyRepository {
public Object read(int id) throws ObjectNotFoundException {
try { ... }
catch (FileNotFoundException ex) {
throw new ObjectNotFoundException(ex)
}
}
}
Because the interface declares the types of errors it can return, the clients of that interface can be consistent and rational. Adding code to handle FileNotFoundException
and SQLException
then the actual implementation may be either, or neither, is not fantastic.
Consider if there were multiple places in the implementation of FileRepository
which could throw FileNotFoundException
. Does that imply each and every one of them implies object not found?
When considering option two, exampleTwoException
, it's important to realise that your catch
block is effectively stating that the effects of whatever error occurred have been mitigated. There are cases where checked exceptions are reasonably ignored, but it is far more likely that a simple
try { ... }
catch (SomeException ex) {
log.error("caught some exception", ex);
}
is actually the result of a developer not considering the consequences of the exception, or the code should have included a FIXME
.
This is even more true when you see catch (Exception ex)
, or the unconscionable catch (Throwable ex)
.
And finally, would you want to be the person to dig through an application finding all the places where you need to add (yet) another catch block to handle a new implementation? Perhaps that's a cause of catch (Exception ex)
...
Always throw exceptions appropriate to the abstraction
exampleOneException
can catchMyBusinessException
and handle it according to their own requirements. Methods callingexampleTwoException
have no way of knowing that anything went wrong. – Transformism