Really, I wasn't going to comment on this holy-war, but I think I will
anyway.
INTRO
-----
Checked exceptions seems like a good idea at first, which is probably
why it's included in JAVA. Unfortunatly it has a few problems, owing
sub-typing and the variance of errors, which makes it tempting to
declare every method "throws Throwable". I will shortly discuss the
problem with current implementations of checked exceptions and then
propose a method which makes checked exceptions beneficial in the way
checked-exception proponents usually argue it is.
EXAMPLE
-------
Let's do an example, C# syntax (this is m*.p*.d*.l*.csharp

with
checked exceptions as in Java.
/** Persist objects, allow retrieval by id */
interface ObjectStore {
int Store(Object);
Object Retrieve(int id);
}
Now for a specific implementation that uses a directory with files for
storage:
public class DiskObjectStore: ObjectStore {
string Directory;
Object Retrieve(int id) {
using ( Stream s =
File.Open(File.Combine(Directory, id.ToString()))) {
// fetch object
...
}
}
}
WHAT SHOULD WE DO WITH THE D***** EXCEPTION
-------------------------------------------
(chorus: WITH THE D***** EXCEPTION)
Obviously File.Open(...) can throw a FileNotFound exception, so should
we catch and discard that? probably not, since we would deprive the
caller of accurate information about what failed.
So, we have to change the declaration of Retrieve:
Object Retrieve(int id): throws FileNotFound {...}
But that doesn't match the interface, and the interface declaration has
to be expanded to include a "throws FileNotFound". This removes the
abstractness of the interface, so you might suggest: "every
implementation of ObjectStore must do some I/O, so we'll let it throw
IOException".
Cutting heels and clipping toes
-------------------------------
This ensnaring argument hides the fundamental problem: you declared the
interface to be implementation-independant in the first place. Even
worse many implementations might NEVER throw an IOException, for example
a test-implementation:
public class MemoryStorage: ObjectStore {
IDictionary objects = new HashTable();
Object Retrieve(int id) throws IOException {
return objects[id]; // cannot throw IOException
}
}
or they might throw a whole new type of exception:
int id_count = 0;
int Store(Object o) {
int id = ++id_count; // ignore concurrency problems for now, ok?
if ( id_count == 0 )
throw new OutOfNamespace(o);
else
objects[id] = o;
}
When it's NOT a feary-tale
--------------------------
In the real world, what you end up with is usually either a global
throws declaration in the interface (and on all the callers of the
interface methods):
Object Retrieve(int id): throws Exception();
Or (nervous ticks starts) every implementation doing:
try {
using ( Stream s =
File.Open(File.Combine(Directory, id.ToString()))) {
// fetch object
...
}
} catch ( Exception e ) {
// an error occured, lot's of info in e, let's throw it away
return null;
}
And a VERY confusing code-style (*mildly* better: Logging errors and
returning null's.)
This is what most JAVA programmers do, atleast in the code i've seen
Wrapping extinguish type
------------------------
The last (and worst) alternative (which unfortunatly is done in a lot of
places in .NET) is to wrap the exception:
try {
using ( Stream s =
File.Open(File.Combine(Directory, id.ToString()))) {
// fetch object
...
}
} catch ( Exception e ) {
throw new ObjectStoreException(e);
}
This prevents the caller from even catching on the actual type of
exception occuring, and thus robs him of a viable retry strategy (unless
he wants to traverse the .Inner's of exceptions looking for the real
problem. This would take HEAPS of code, especially if more than one
wrapping is done). It actually removes the whole point of having Types
exceptions
*** NOTE: .NET Delegate's do exception wrapping, making catch on a
delegate-invocation a VERY delicate thing to try.
So, while checked exceptions have some nice properties, they really are
tedious to work with when polymorphism is used, since they precisely
expose the implementation differences that you wished to ignore by using
polymorphism.
JAVA
----
Now, zooming in on JAVA: JAVA has excepted that having every exception
as checked is too tedious. In JAVA there are RuntimeException's that may
be thrown *without* declaration in throws statements, and we may even
declare such an exception ourselves, BUT the class heirarchy is used to
indicate whether an exception should be checked or not.
This is higly unfortunate, since it deprives the programmer of the
ability to decide seperatly from situation to situation how "important"
a specific type of exception is, whether it should be catched or not by
the caller.
PROPOSAL
--------
If it was possible to declare "throws" statements on functions and
require callers to either handle or declare the exceptions themselves
(no class-hierarchy spec. of what should be checked here), AND it was
possible to "uncheck" an exception, allowing the caller to knowingly
pass a previously checked exception from a function as unchecked, now
that WOULD be usefull.
It would allow us to write the interface from above in the way we
wanted, without any throws qualifier, but it would allow File.Open to
declare that a FileNotFoundException could reasonably occur and that the
caller should do something about it.
The resulting code would be something like (with a newly invented syntax
for "unchecking", could probably be MUCH ):
using ( Stream s =
uncheck(File.Open(File.Combine(Directory, id.ToString()))),
FileNotFound)
{
// fetch object
...
}
The "uncheck" generates no code, and no catch-handler, it simply
instructs the compiler that the programmer knows that the call to
File.Open may cause an exception, but that should be accepted at
runtime, rather the provably handled by the caller.
CONCLUSION
----------
In this way the intrusion of checked exceptions may be kept to a
tolerable level, and cheked exceptions declarations can be actually
utilized to indicate "hey mate, this might fail, and you better have
something up your sleeve if it does".