In Java, why favor:
to:In a dynamically typed language I would :-) But seriously, something troubles me about this because it often seems to reveal implementation in some way. Say I have:
class Document { public void store() throws IOException { outputStream_.write(this); } }and then I refactor it to:
class Document { public void store() throws SQLException { database_.write(this); } }All the code that calls store() has to be changed. But it seems that none of that code should really care how store() is implemented.
In store, catch SQLException and ReThrow? as IOException?
How do you know the SQLException has anything to do with input or output? But perhaps I'm coming at this from the wrong angle. I feel that it's only worth declaring checked exceptions in the method signature if you might expect those exceptions without having any idea what the implementation is. Otherwise you're exposing implementation. Saying "throws IOException" may be OK for store() here by that test, but what about the method that calls store()? What about the method that calls that? It just seems that you end up spending a lot of time dealing with specific exceptions, when almost all the time all you want to do is let them percolate up if they happen.
Confusion is arising here because of a mismatch between levels of abstraction. Document and its store operation are at the domain level, whereas OutputStream? and Database, and their corresponding operations, are at the infrastructure level. Document.store should only throw domain-level exceptions (such as a StoreException?); infrastructure-level exceptions from lower-level components should be caught and a domain-level exception thrown instead.
So, in the example above:
class Document { public void store() throws StoreException? { try { getOutputStream().write( this ) ; } catch( IOException e ) { throw new StoreException?( e ) ; // using a chained exception } } }Where store could then be changed to:
class Document { public void store() throws StoreException? { try { getDatabase().update( this ) ; } catch( SQLException e ) { throw new StoreException?( e ) ; // using a chained exception } } }This avoids the problem of having to change the error-handling in the callers of store(), and is aesthetically cleaner. It does, however, involve more code, all of it boilerplate.
I consider the need for this exception-conversion boilerplate a necessary evil. Sometimes i take the short-cut of considering IOException to be a domain-level exception (meaning "something went wrong in the lower layers, so this operation couldn't be completed"), and just throw that on. This does mean that if i change to a database implementation, i have to wrap the SQLException in an IOException, though! One thing i think i would have done if i'd been JamesGosling is to have an abstract exception class SystemException? or InfrastructureException?, and have IOException, SQLException, RemoteException, etc, descend from that, so i could declare it in throws clauses of methods which do something external (such as IO or DB access) without tying myself to a particular implementation. -- TomAnderson
I guess the difference here is that the exception-conversion code bothers me quite a bit. I find that all those try...catch things make the code much harder to read, and since they're not really doing anything substantive I am frequently tempted to just use ThrowsExceptionByDefault. -- ChristianTaubman
If we had customisable syntax, we might build a throws ... as construct, so we could write:
class Document { public void store() throws SQLException as StoreException? { getDatabase().update( this ) ; } }And it would just DoTheRightThing.