The 'ignoreslist' exception handling proposal.

Reinier Zwitserloot reinier at zwitserloot.com
Wed May 20 06:26:07 PDT 2009


I'm more convinced than ever that java really needs this change,  
sooner rather than later.

My arguments in favour:

  1. Ruslan totally misunderstood my proposal, and also appears to  
misunderstand checked exceptions on the JVM level in general. This is  
in my experience an extremely common problem amongst java programmers,  
and this is very bad news for trying to interop javac-produced class  
files with scalac, groovy, JRuby, Python, and other languages' class  
files. By adding the concept of sneakyThrow to java, we teach java  
programmers that checked exceptions are only a debugging aid offered  
by javac, and NOT a guarantee by the JVM. This isn't a philosophical  
change at all: It's realism. In today's JVM world, you just cannot  
rely on that as a java programmer, and yet most java programmers do.

  NB: Ruslan and everyone else that's confused - I'll send an  
explanatory email in a few minutes to explain how it all works.

  2. Joe Darcy, project coin, and sun routinely defend choices for the  
java platform by stating that java is designed for the realistic,  
human (e.g. fallible) programmer. We defend not adding operator  
overloading by stating that people will abuse it (FWIW, I agree with  
this mentality). And yet java (the language, not the VM!)'s current  
method of handling checked exceptions is geared towards the mythical  
perfectionist programmer! That makes no sense at all. I'll back up  
this point:


  #1: The JVM cannot, and does not, guarantee that the exceptions that  
fall out of a method call are only Errors, RuntimeExceptions, and any  
exception types declared by that method. That's simply not how the JVM  
works, and yet most java programmers erroneously think that it is.  
Being realistic for a moment, sneakyThrows occur all the time; you can  
have a class file mismatch (compiled against a different version of  
the class file vs. the version of that class file at runtime), you can  
be using a class compiled by scala, groovy, JRuby, Jython, or any  
other non-java programming language, or someone used a sneakyThrow  
method.

Realistically, then, this isnt a philosophical change at all. We're  
just acknowledging something that already happened.

  #2: People get exception hierarchies wrong. All the time. The new  
String(bytes, encoding) constructor is flawed, for example. The right  
solution would be to have a Charset class and appropriate constructor  
that uses it (which string now has, since java 1.6, but the point to  
take home here is that the java core team it self, with all the  
lessons they knew by the time java 1.5 was developed, and with all  
that reviewed, still screwed it up), and preferably also a  
Charset.UTF8 constant, which java still doesn't have. Right now we  
have the weird situation that new String(bytes, "foo"); throws a  
checked exception, but new String(bytes, Charset.forName("foo")),  
which seems semantically equivalent, throws an unchecked exception.  
That cannot be the right design; we have it today because of the need  
for backwards compatibility. We must acknowledge that java's exception  
handling isn't perfect.

There isn't even a proper definition of what constitutes a good  
situation for a checked exception, and what doesn't. Some people say  
that checked exceptions are appropriate only for legitimate  
alternative return values, where it would be a clear bug if a caller  
doesn't handle this code, in virtually all imaginable scenarios, such  
as an InsufficientBalanceException for a banking app's  
transferFundsFromPersonToPerson(Person A, Person B) method. In this  
view, IOException is mistyped (because there are whole hosts of  
situations where IOExceptions are either extremely unlikely, or  
likely, but there's nothing you can do - it is truly a program error  
and not an alternative return scenario; the only viable action is  
quitting, which an unchecked exception can do just as well). Then  
there's the view that a checked exception is appropriate anytime an  
exception is remotely likely to occur. In this view, Integer's  
parseInt's NumberFormatException is misclassed, because obviously non- 
numeric input is likely, and yet NFEx is unchecked.

There is, in fact, no philosophy about checked and unchecked  
exceptions that would classify the various exceptions in the java core  
libraries the way they are in real life. It's a grab bag; some are  
checked, some are unchecked, and there's only a vague philosophy  
behind the choices.

InputStream.close() throws a checked exception which most programmers  
find very questionable. Almost certainly a mistake, in practice (even  
if in theory it makes sense and is nicely symmetric with  
OutputStream's close, which does entirely appropriately throw  
IOException - at least as appopriate as write()'s throws clause).

Proper use of the new String(bytes, encoding) constructor (where the  
input encoding is actually a variable and not a string literal) throws  
a checked exception, but Integer.parseInt() doesn't. yet, both are  
trying to parse a string where some forms are legal and some forms  
aren't. There's no reason for the dichotomy here; it's random.

SQLException has only recently seen some work to make it a more usable  
construct, though the current situation amongst JDBC drivers remains  
troublesome. There are also many many mistakes that the entire  
exception hierarchy concept can make happen. Fine case in point:

new String(bytes, "UUTF-8"); throws an UnsupportedEncodingException,  
which is a subclass of IOException. What, exactly, does this error  
have to do with I/O? Nothing whatsoever. The idea that  
UnsupportedEncodingException is a subclass of IOException is very  
questionable.

We can also look at the other extreme and complain about the insane  
amount of exceptions that can fall out of reflection related calls.

Bottomline: Let's be realistic - exception handling in java isn't  
perfect, and it never will be. Trying to protect people from abusing  
the throwing of exceptions by forcing rigid checked exception handling  
is a failed experiment, and the fact that just about every JVM  
language other than java itself doesn't have checked exceptions at all  
means we need a way to explicitly say to javac: I know better than you  
do, in this instance. Realistically, the number of situations where  
javac's advice is just wrong is formidable, and its making us write  
bad code.


Mark, your proposal does indeed attempt to deal with this issue in a  
less philosophically drastic fashion, but I think the end result just  
isn't as good. Consider again Ruslan's typoed UUTF-8 example:

If we wrap it, we're just hiding the cause behind a generic  
InternalError, AssertionError, or RuntimeException. I've seen them  
all. There's actually a decent unchecked alternative available  
(UnsupportedEncodingError) but that's the exception (heh, heh) rather  
than the rule. Sure, there's getCause(), but 'cause hell' is already  
causing stack traces nearing a hundred pages long, and a continuing  
unsolved problem in java land is finding the one cause in the massive  
onslaught that helps you get to real issue at hand. In this situation,  
the rewrap is needless noise in the exception chain.

Without either your or my proposal, we get the worst of both worlds:  
We rethrow, adding noise to the runtime information, and we also add 4  
to 5 lines of noise to the code with a semantically pointless try/ 
catch block that rethrows the UnsupportedEncodingEx into something else.

  --Reinier Zwitserloot
Like it? Tip it!
http://tipit.to






More information about the coin-dev mailing list