The 'ignoreslist' exception handling proposal.

Reinier Zwitserloot reinier at zwitserloot.com
Wed May 20 09:22:38 PDT 2009


Remi,

While charsets are imported from outside the system, there are a few  
that are guaranteed by the JVM specs. A 'JVM' that throws an  
UnsupportedEncodingException on:

  new String(new byte[] { 65, 66, 67}, "US-ASCII");

  Isn't a JVM at all, because the spec says that US-ASCII *MUST* be  
present.

You said it: If its a developer mistake, then it should be a runtime  
exception. Mistyping 'US-ASCII' is a developer mistake.

Also, Integer.parseInt(SomeUserInput)'s NumberFormatException is  
clearly NOT a developer mistake, yet that is a runtime exception.  
Grabbing a static file out of your deployed jar file via  
Class.getClassLoader().getResourceAsStream(), and reading this file  
caused an issue, you get a checked IOException, eventhough this is  
clearly the developer's error in writing his deploy script. Said  
differently, if reading local resources ought to fail with a checked  
exception, then every attempt to load a class should be a checked  
exception as well (after all, classes are just local resources as well!)

So, yes, java's exception hierarchy is a mess sometimes.

The idea behind making this point is that javac ignores reality and  
treats the checked exception system as holy and infallible - that is,  
you can NOT tell javac to stop whining about not handling checked  
exceptions. There are numerous situations where javac insists you  
handle an exception, even though as developer you know there is either

   A) a chance akin to the odds of a random OutOfMemoryError that the  
exception will actually occur, and is thus not worth the boilerplate  
(and due to the near impossibility of it, you have no sane route for  
handling the problem, so all you can do is rewrap or log it and fail),  
such as UnsupportedEncodingEx, or the IOExceptions falling out of  
resources loaded via ClassLoader.getResourceAsStream, or IOExceptions  
on FilterXStreams that are backed by a ByteArrayXStream, or

  B) You -know- it'll work itself out, such as for example throwing a  
checked exception out of a Runnable used as a Thread (all exceptions  
falling out of run() are handled by the thread unhandled exception  
mechanism, regardless of whether they are checked or not) - usually  
due to bad API design where an interface method you're supposed to  
override did not have a 'throws Exception' tacked on, and it can no  
longer be changed now due to backwards compatibility requirements.  
Example: java.lang.Runnable, and much in the HTTPServlet class.

I posit that these situations are both common and not otherwise  
solvable.

Therefore, I propose we are given a tool to more easily work around  
the failings of the hierarchy. Right now lots of java programmers  
would rather 'not have checked exceptions at all' because its so much  
hassle. By giving a cheaper way out of the system when it fails, we  
can let it stick around for all the times when it does work properly  
and catches us forgetting to handle an important exceptional situation.

Being realistic, java programmers tend to -hate- the checked exception  
system with a passion. Unfortunately, hate translates into not paying  
attention to it. By crying wolf so much, we're trying java programmers  
to just let their IDEs fill in the boilerplate, or toss 'throws  
Exception' on everything just so it'll compile. This is not a good  
thing. Perfectionists would handle everything properly, but that's  
just not what's happening.

That's my point; even though the opportunity for abuse is there, when  
you take into account that programmers aren't perfectionists and won't  
pen out a complete and well thought out try/catch block just to handle  
something they -think- is almost impossibly unlikely to happen, with  
ignore lists java code will be better on average.

Compouding this already sizable problem:

Out of the box, eclipse 'quickfixes' an uncaught exception by writing  
a catch block that prints the exception to standard error and then  
just continues silently. This is a bit like ON ERROR RESUME NEXT in  
visual basic. At the risk of peeving off eclipse developers: That's  
just retarded. I fixed the template in my eclipse, but how many  
programmers do that themselves? When I let eclipse quickfix an  
UnsupportedEncodingException, or an IOException on closing an  
InputStream, I usually just let this stand:

try {
    inputStream.close();
} catch ( IOException e ) {
    //TODO Template catch block
    throw new RuntimeException(e);
}

eventhough that is so clearly inferior to just sneakyThrowing the  
IOException up the chain.


less contentious alternative: add a sneakyThrow static method to  
Throwable in java core, and work with IDE vendors to replace the  
default quickfix for uncaught checked exceptions to:

try {
     inputStream.close();
} catch ( IOException e ) {
     Throwable.sneakyThrow(e);
}




  --Reinier Zwitserloot



On May 20, 2009, at 17:02, Rémi Forax wrote:

> Reiner, the problem is not new String(byte, Charset) but
> the fact that Charset.forName() doesn't use a checked exception.
>
> The philosophy behind exception is:
> - If it's a developer mistake, it's a runtime exception
> - If it's an error that comes form outside the Java platform,
> i.e an error that can not be predicted without some elements from  
> outside
> the Java platform,  it's a checked exception.
>
> Depending on the installation, some Charsets are available or not,
> this is clearly an error that comes form outside of Java world so
> it should be handled with a checked exception.
>
> Ok, it's a mistake
> but I don't understand  how  it's related to your proposal ?
>
> Rémi
>
> Reinier Zwitserloot a écrit :
>> 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