Checked Exceptions do not exist on the JVM.

Reinier Zwitserloot reinier at zwitserloot.com
Thu May 21 01:33:21 PDT 2009


Joe, perhaps you missed it in all the hullabaloo about the  
Class.newInstance() and class construction holes, which aren't the  
interesting reasons to add dont-care. These are:

1. Java actively wants to integrate better with other languages. We  
should extend this principle to exception handling.

2. The exception hierarchy has many flaws in it. As far as I can tell,  
this is undisputed, and I've already sent a very long email listing  
many examples. The problem with this, is that its causing average java  
programmers to strongly dislike the exception system, and as a result,  
to write bad code. I've also explained the how and why of this in  
depth. Adding a dont-care option would give programmers an easy way  
out when, either through a flawed hierarchy or an unexpected  
situation, they are forced into dealing with an exception they know  
either can't happen or can safely be thrown onwards but e.g. an  
interface restricts their throws lists.

3. In situations where that does happen (such as IOExceptions falling  
out of resources loaded via ClassLoader.getResourceAsStream, or  
UnsupportedEncodingExceptions falling out of new String(bytes,  
"UTF-8"), or throwing a checked exception out of a Runnable.run()  
method passed to a thread), java programmers are currently forced into  
writing boilerplate that actually makes their code worse as well! -  
such exceptions get either wrapped, which is bad, because trying to  
find the signal in the noise of an exception with 20 causes (it  
happens, all the time, in frameworks) can already be a formidable  
problem, and wrapping an exception just for the sake of retrofitting  
it to an unchecked form ALWAYS adds 1 level of noise to the chain. The  
alternative is that they get swallowed or printed to the console,  
which is even worse, for obvious reasons. So, it's that, -and- the  
boilerplate, -and- the alienation of the average java programmer to  
the exception system. All bad things, and it happens quite often (that  
is; situations where you are forced by javac to deal with a checked  
exception where javac is essentially wrong, and you really should not  
be forced into dealing with it).

4. I offered a less contentious alternative: Add  
Throwable.sneakyThrow(Throwable t) to java.lang.Throwable, and lobby  
IDEA, Eclipse, and the NetBeans team to change the way the 'quickfix'  
feature works when trying to quickFix the 'uncaught  checked  
exception' compiler error, by wrapping in a try/catch block and sneaky  
throwing the exception, which is superior. (clearly, to me, and I  
haven't heard anybody give me a reason or alternative why I'm wrong)

  --Reinier Zwitserloot



On May 20, 2009, at 20:02, Joseph D. Darcy wrote:

> Well said Tom.
>
> Additionally, on the point of interfacing with other language in  
> Java, the possibility of having what are in Java checked exceptions  
> thrown by the caller argue for Dynamic call sites to be regarded as  
> being able to throw Exception so that the existing Java semantics  
> and checking can be preserved.
>
> -Joe
>
> Tom Ball wrote:
>> I don't consider "Ruslan, you've got it all wrong" an example of  
>> opening a
>> "perfectly civil email", it sounds like bullying.  This isn't  
>> slashdot, it's
>> a forum for discussions on possible small improvements to the Java
>> language.  So I shouldn't have responded with the same tone as your  
>> message,
>> and for that I apologize to the wider group.
>>
>> An "it's possible to write bad code" argument doesn't refute other  
>> people's
>> valid arguments.  Developers are always capable of writing bad  
>> code, and
>> anyone who designs a language or runtime where it's impossible will  
>> have
>> trouble finding adoption because it's too restrictive.  Who cares  
>> if sneaky
>> throw works?  If a library abuses that hack, don't use it.
>>
>> The purpose of throws clauses is to better describe the contract a  
>> method
>> offers for its callers:  it only accepts parameters of a certain  
>> type, it
>> returns an instance of a specific type, and under certain (hopefully
>> documented) conditions it may throw specific exceptions.  Checked  
>> exceptions
>> are intended to call out specific exceptions that the library writer
>> believes the caller should be able to recover from, and therefore  
>> should
>> handle or forward.  If a library writer subverts the contract of  
>> his own
>> method, well, that's a shame but doesn't necessarily call for  
>> changes to the
>> language.  If a non-Java language has libraries which throw what  
>> are in Java
>> checked exceptions, then write Java code which does more checking  
>> at runtime
>> since that's apparently what the other language requires (or don't  
>> use those
>> libraries).
>>
>> This isn't a problem, and so there is nothing about it that needs  
>> fixing in
>> Java 7.
>>
>> Tom
>>
>> On Wed, May 20, 2009 at 9:39 AM, Reinier Zwitserloot <
>> reinier at zwitserloot.com> wrote:
>>
>>
>>> Tom, here are two class files:
>>>
>>> Foo.class
>>> Bar.class
>>>
>>> which one adheres to the JLS checked exception model, and which  
>>> one has
>>> methods that leak checked exceptions without declaring them?
>>>
>>> I can't tell. Can you?
>>>
>>> You can quote me JLS specs all day long, mis-interpret my words  
>>> and write
>>> in a denigrating fashion, but none of that changes the plain fact:
>>>
>>> You CAN NOT RELY ON THROWS CLAUSES. Checked exceptions can fall  
>>> out of
>>> *any* method *anywhere*. Period. I thought when I mentioned "checked
>>> exceptions in situations where you didn't expect them", it was  
>>> obvious from
>>> context that I meant: checked exceptions falling out of methods  
>>> that don't
>>> declare them, but apparently I need to spell this out for you.
>>>
>>> You also conveniently forgot that, even if you're only using  
>>> javac, you can
>>> still get sneakyThrow: You can expicitly sneakyThrow via class file
>>> construction or using the .newInstance() trick, or get accidental
>>> sneakyThrow when compiling against v1 of a class, but running with  
>>> v2 (which
>>> added a bunch of checked exceptions). Point me to the entry in the  
>>> JLS that
>>> makes this impossible, please. It doesn't exist, so, take your  
>>> pedantic
>>> whinging about "JLS spec", print it out, and eat it*.
>>>
>>>
>>> *) I get annoyed when people write 'WTF' and 'I give up' in  
>>> response to a
>>> perfectly civil email.
>>>
>>>
>>> Example:
>>>
>>> public class Foo {
>>>   public static void foo() {}
>>> }
>>>
>>> as Foo.class
>>>
>>> and this:
>>>
>>> public class Bar {
>>>   public static void main(String[] args) {
>>>       Foo.foo();
>>>   }
>>> }
>>>
>>> as Bar.class.
>>>
>>> Then edit Foo.java to this and recompile (but don't recompile  
>>> Bar.java!),
>>> then rerun Bar:
>>>
>>> public class Foo {
>>>   public static void foo() throws java.io.IOException {
>>>       throw new java.io.IOException("Apparently this will make Tom  
>>> Ball's
>>> head explode. Whoops!");
>>>   }
>>> }
>>>
>>> ... and behold! Tom Ball's head explodes.
>>>
>>> --Reinier Zwitserloot
>>>
>>>
>>>
>>>
>>> On May 20, 2009, at 18:11, Tom Ball wrote:
>>>
>>> On Wed, May 20, 2009 at 6:35 AM, Reinier Zwitserloot <
>>>
>>>> reinier at zwitserloot.com> wrote:
>>>>
>>>> Checked exceptions do not exist.
>>>>      Sure they do, but they are a Java language concept, not a  
>>>> JVM one.  The
>>>> same
>>>> is true for lots of other Java language features.
>>>>
>>>> That's right. They don't. There's no such thing. It's all a  
>>>> figment of
>>>>
>>>>
>>>>> javac.
>>>>>
>>>>>
>>>> You must mean that checked exceptions are defined by the Java  
>>>> Language
>>>> Specification.  javac is just a compiler that adheres to the JLS,  
>>>> like
>>>> many
>>>> other Java compilers.
>>>>
>>>>
>>>> The JVM does not have checked exceptions. All it knows about is
>>>>
>>>>> 'throws clauses', which are part of a method's signature and  
>>>>> have ZERO
>>>>> impact on how a JVM runs a class file. As far as the JVM is  
>>>>> concerned,
>>>>> throws clauses are just a comment. Utterly ignored by javac.
>>>>>
>>>>>
>>>> No compiler can ignore the throws part of a method signature and  
>>>> be a
>>>> valid
>>>> Java language compiler.  Having worked on javac, I know for a  
>>>> fact that
>>>> whether a class symbol is read from a source or class file, its  
>>>> methods
>>>> include all defined throws clauses and enforces their use as the  
>>>> JLS
>>>> dictates.  The javac source is publicly available -- check out  
>>>> the Symbol
>>>> and ClassReader classes.
>>>>
>>>>
>>>> You can
>>>>
>>>>> strip every single one out of every class file and run an app,  
>>>>> and the
>>>>> execution of it would be 100% the same. The only change would be  
>>>>> in
>>>>> java.lang.reflect's getExceptionTypes() method.
>>>>>
>>>>> I repeat: The JVM *DOES* *NOT* know about checked exceptions.  
>>>>> The JVM
>>>>> assumes all exceptions are unchecked, and assumes that javac  
>>>>> sorted it
>>>>> all out. This is flawed, obviously, because javac isn't the only  
>>>>> tool
>>>>> out there that can make class files.
>>>>>
>>>>>
>>>> Flawed?  The Java Language and the JVM have different  
>>>> specifications; the
>>>> JLS defines checked exceptions, and the JVMS doesn't.  The JVM  
>>>> support
>>>> many
>>>> different languages besides Java, most of whom don't define checked
>>>> exceptions.  That's not a flaw, but support for language  
>>>> independence.
>>>> The
>>>> reason the JVM considers all exceptions as runtime exceptions is  
>>>> because
>>>> it's the runtime.
>>>>
>>>>
>>>> This is similar to generics in
>>>>
>>>>> objects, which is also a figment of javac's imagination: On the  
>>>>> JVM
>>>>> level, erasure occurs - that info just isn't there. If you want,  
>>>>> you
>>>>> can name this 'checked exception erasure'. The notion that  
>>>>> exceptions
>>>>> are checked is erased during compilation.
>>>>>
>>>>>
>>>> Not true.  Yes, the JVM doesn't support generics, but classfiles  
>>>> contain
>>>> all
>>>> information needed to correctly compile classes which use them.   
>>>> The same
>>>> is
>>>> true for exceptions, as the JLS requires.
>>>>
>>>>
>>>> The only application in existence that cares about this 'throws
>>>>
>>>>> clauses' comment in class files is the application called 'javac'.
>>>>>
>>>>>
>>>> You are going to make engineers support other Java compilers  
>>>> upset with
>>>> this
>>>> incorrect assumption.  They all have to support checked  
>>>> exceptions, or
>>>> they
>>>> aren't Java compilers.
>>>>
>>>> It gets worse: JRuby, groovy, Jython, and scala, they ALL feature
>>>>
>>>>
>>>>> *ZERO* checked exception clauses on methods. They just generate  
>>>>> class
>>>>> bytecode with throw statements without caring about the throws  
>>>>> clause
>>>>> on methods.
>>>>>
>>>>>
>>>> Worse?  Different languages make different trade-offs between  
>>>> compile and
>>>> runtime checking, and it's a matter of personal preference what  
>>>> trade-offs
>>>> you want.  There is no "better" or "worse", which is why the JVM  
>>>> doesn't
>>>> require one or the other.
>>>>
>>>>
>>>> This is why, in java, anytime you interop with code not
>>>>
>>>>> compiled by javac, you can get checked exceptions in situations  
>>>>> where
>>>>> you didn't expect them.
>>>>>
>>>>>
>>>> WTF?  If you are calling libraries written in other languages,  
>>>> those
>>>> libraries throw exceptions as defined by their language  
>>>> specifications.
>>>> If
>>>> you don't like the contracts of those libraries, don't use them.
>>>>
>>>> I give up,
>>>> Tom
>>>>
>>>>
>>>> Millions of classes generated by those
>>>>
>>>>> compilers are out there right now throwing checked exceptions that
>>>>> escape from methods that do NOT have a 'throws X' clause on them.
>>>>>
>>>>> In other words: There is *NO* guarantee whatsoever in the JVM that
>>>>> checked exceptions can only occur in situations where they are
>>>>> explicitly declared. If your code relies on it, then your code  
>>>>> is buggy.
>>>>>
>>>>>
>>>>> Secondly, you mentioned that java wraps exceptions that occur in  
>>>>> the
>>>>> constructor with Class.newInstance() into an
>>>>> ExceptionInInitializerError. That's not what happens at all; That
>>>>> exception (ExceptionInInitializerError) is for static  
>>>>> initializers and
>>>>> object initializers, not constructors. A practical example will  
>>>>> make
>>>>> this clear. Take the following code, save it in Foo.java,  
>>>>> compile it,
>>>>> and run it:
>>>>>
>>>>> import java.io.IOException;
>>>>>
>>>>> public class Foo {
>>>>>  public static class Test {
>>>>>      public Test() throws IOException {
>>>>>          throw new IOException();
>>>>>      }
>>>>>  }
>>>>>
>>>>>  //Note how I didn't add 'throws IOException' in the next line!
>>>>>  public static void main(String[] args) throws
>>>>> InstantiationException, IllegalAccessException {
>>>>>      Test x = Test.class.newInstance();
>>>>>  }
>>>>> }
>>>>>
>>>>>
>>>>> The result is an IOException. Not a ObjectInstantionException  
>>>>> with an
>>>>> IOException as cause; no - an IOException. You can doublecheck  
>>>>> this
>>>>> by  wrapping the newInstance in a try/catch (Throwable t) block  
>>>>> and
>>>>> printing the class name of the resulting t.
>>>>>
>>>>> Thirdly, you misunderstand what my ignoreslist proposal does: It  
>>>>> does
>>>>> not silently swallow the exception, that'd be a very stupid way of
>>>>> handling exceptions that you don't -think- can occur. An  
>>>>> ignoreslist
>>>>> has zero effect on the execution of a method body. The only  
>>>>> difference
>>>>> is that, unlike a throws clause, you don't burden any of the  
>>>>> methods
>>>>> that call you to handle the listed exception. It's a signal to  
>>>>> javac
>>>>> to disable its rigid checking of making sure any checked exception
>>>>> thrown in the body is also present in that method's 'throws'  
>>>>> clause.
>>>>> It's exactly analogous to adding the 'throws' clause, compiling  
>>>>> the
>>>>> code, then using a hex editor to remove the throws clause.
>>>>>
>>>>>
>>>>> You're also mistaken in retrofitting Runnable with 'throws  
>>>>> Exception':
>>>>> That'll break many thousands of classes out there in real life.  
>>>>> You
>>>>> forget that people don't just make Runnables and hand them off to
>>>>> java.lang.Thread; there's plenty of code out there that *receives*
>>>>> Runnables, and that code presumes that its run() method does not  
>>>>> throw
>>>>> any checked exceptions. It would be backwards compatible  
>>>>> (because, as
>>>>> I said, the JVM doesn't know about these things), but it  
>>>>> wouldn't be
>>>>> source compatible; recompiling your code would all of a sudden  
>>>>> produce
>>>>> errors. e.g:
>>>>>
>>>>> public void myMethod(Runnable r) {
>>>>>  r.run();
>>>>> }
>>>>>
>>>>> would no longer compile, because all of a sudden 'r.run();' can  
>>>>> throw
>>>>> Exception, which I need to catch, or declare as thrown (or, if  
>>>>> this
>>>>> proposal is accepted, declare as sneakyThrown/ignore).
>>>>>
>>>>>
>>>>> --Reinier Zwitserloot
>>>>>
>>>>>
>>>>>
>>>>> On May 20, 2009, at 07:01, Ruslan Shevchenko wrote:
>>>>>
>>>>> Here's a trivially simple change to implement, though it has  
>>>>> some far
>>>>>
>>>>>>> reaching repercussions for java as a whole:
>>>>>>>
>>>>>>> change the 'rethrows' clause to an ignores clause: Any checked
>>>>>>> exception in the ignores list may be thrown from the method  
>>>>>>> body, but
>>>>>>> is NOT part of the method's checked exception list. It is  
>>>>>>> hence also
>>>>>>> not part of the signature, just like sticking a 'synchronized'
>>>>>>> keyword
>>>>>>> on a method isn't part of its signature, but an implementation
>>>>>>> detail.
>>>>>>>
>>>>>>>
>>>>>>> example:
>>>>>>>
>>>>>>> Runnable r = new Runnable() {
>>>>>>>  public void run() ignores UnsupportedEncodingException {
>>>>>>>      String x = new String(inBytes, "UTF-8");
>>>>>>>  }
>>>>>>> };
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> And how actually debug such statements ?
>>>>>> //In this example, run() method do nothing, so look on slightly
>>>>>> complex
>>>>>> primer:
>>>>>>
>>>>>> class X
>>>>>> {
>>>>>> String x;
>>>>>>
>>>>>> public void setString(byte[] inBytes)
>>>>>>                               ignores  
>>>>>> UnsupportedEncodingException
>>>>>> {
>>>>>>        x = new String(inBytes, "UUTF-8");
>>>>>> }
>>>>>>
>>>>>> public void setAndPrintString(byte[] inBytes)
>>>>>> {
>>>>>> setString(inBytes);
>>>>>> System.out.println("received:"+x);
>>>>>> }
>>>>>>
>>>>>>
>>>>>> }
>>>>>>
>>>>>> Assume, that programmer wrote (by error) "UUTF-8" instead "UTF-8"
>>>>>>
>>>>>> Output of programm will be "received:null".
>>>>>> Reasons for null x in complex programs can be millions.  
>>>>>> Debugging with
>>>>>> ignoring exception will be extremally hard.
>>>>>>
>>>>>> I. e. wrapping checked exception to runtime is more or less  
>>>>>> normal;
>>>>>> ignoring will become a very big paint-point.
>>>>>>
>>>>>> Alternative to ignore can be call default thread exception  
>>>>>> handler.
>>>>>> (handle keyword ?)
>>>>>>
>>>>>> This sounds like blowing up the entire point of checked  
>>>>>> exceptions,
>>>>>>
>>>>>>> but with java moving towards more interoperation with other  
>>>>>>> JVM based
>>>>>>> languages, which almost always throw checked exceptions without
>>>>>>> actually declaring them (Jython, JRuby, Scala -- I can't think  
>>>>>>> of a
>>>>>>> single one other than java itself that has the notion of checked
>>>>>>> exceptions in the first place). If the whole 'java is like the
>>>>>>> assembler of the JVM' story is going to hold water, something  
>>>>>>> like
>>>>>>> this is needed.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> Yes, this is interesting problem.
>>>>>> In theory we have UndeclaredThowableException, but .. wrapping  
>>>>>> each
>>>>>> invocation of foreign language into it is ineffecient.
>>>>>>
>>>>>> But InvokeDynamic can throws any throwable in declaration, so  
>>>>>> this
>>>>>> problem
>>>>>> can be solved (on other level)
>>>>>>
>>>>>> For some further support: I doubt anyone seriously holds the  
>>>>>> position
>>>>>>
>>>>>>> that java's checked exception experiment was an unmitigated  
>>>>>>> success.
>>>>>>> There are pain points, here and there. The above example in fact
>>>>>>> includes two of them: The fact that runnable throws no checked
>>>>>>> exceptions is clearly a mistake in regards to threading, because
>>>>>>> Thread objects clearly should handle ANY exception falling out  
>>>>>>> of its
>>>>>>> run() method (and it in fact does, via the unchecked exception
>>>>>>> handler
>>>>>>> mechanism), and there's a general mismatch between main(),  
>>>>>>> which MAY
>>>>>>> throw exceptions, and run(), which may not, even though they  
>>>>>>> are both
>>>>>>> 'thread start points'. Another mistake is
>>>>>>>
>>>>>>>
>>>>>> Adding throw exception to signature of Runnable.run does not  
>>>>>> broke
>>>>>> anything in existent code, so this can be nice library change.
>>>>>>
>>>>>> UnsupportedEncodingExceptions, a checked exception, resulting  
>>>>>> from a
>>>>>>
>>>>>>> call to converting byte arrays to strings using an encoding  
>>>>>>> that is
>>>>>>> guaranteed by the JVM using a string literal, such as "UTF-8".  
>>>>>>> The
>>>>>>> appropriate exception would be UnsupportedEncodingError("This  
>>>>>>> JVM is
>>>>>>> broken. Charset UTF-8 missing. Reinstall it") - which is  
>>>>>>> unchecked.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> Or porgrammer can write UUTF-8 instead UTF-8. It's happens ;)
>>>>>>
>>>>>> In other words, while I'm not ready to write off checked  
>>>>>> exceptions
>>>>>>
>>>>>>> as
>>>>>>> a whole, I am more than ready to acknowledge that the  
>>>>>>> programmer more
>>>>>>> often knows better than the compiler, compared to how often the
>>>>>>> programmer, in laziness or inexperience, abuses this freedom.
>>>>>>> Especially considering that, on the JVM, you really DONT get  
>>>>>>> checked
>>>>>>> exceptions guarantees; any class compiled by anything other than
>>>>>>> javac
>>>>>>> is free to throw checked exceptions without declaring them,  
>>>>>>> and even
>>>>>>> in javac you can technically do this using 'sneaky throw'  
>>>>>>> libraries,
>>>>>>> which use various workarounds, one of which will NEVER go away*.
>>>>>>> We're
>>>>>>> not losing much here, in other words. In fact, the way javac  
>>>>>>> works,
>>>>>>> inexperienced java programmers may erroneously assume that  
>>>>>>> checked
>>>>>>> exceptions couldn't possibly happen unless the checked  
>>>>>>> exception is
>>>>>>> declared. This is not the case.
>>>>>>>
>>>>>>> *) The one that will never go away is  
>>>>>>> java.lang.Class.newInstance(),
>>>>>>> which is functionally defined to sneakily throw on any  
>>>>>>> exceptions
>>>>>>> thrown by the constructor, checked or not, and does NOT wrap  
>>>>>>> them up
>>>>>>> into a wrapper the way java.lang.reflect.Method.invoke() does.  
>>>>>>> This
>>>>>>>
>>>>>>>
>>>>>> Its wrapped to java.lang.ExceptionInInitializerError
>>>>>>
>>>>>>
>>>>>> cannot change without breaking backwards compatibility. Another  
>>>>>> one
>>>>>>
>>>>>>> that will never go away is constructing a class on the fly that
>>>>>>> sneaky
>>>>>>> throws, loading it with ClassLoader's load from bytes  
>>>>>>> mechanism, and
>>>>>>> executing it.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> And what is JVM behaviour in such case ?
>>>>>>
>>>>>>
>>>>>>
>>>>>>> Attempting to solve this issue with a library requires rather  
>>>>>>> a lot
>>>>>>> of
>>>>>>> ugly boilerplate:
>>>>>>>
>>>>>>> import static Utils.sneakyThrow;
>>>>>>>
>>>>>>> Runnable r = new Runnable() {
>>>>>>>  public void run()  {
>>>>>>>      String x;
>>>>>>>      try {
>>>>>>>          x = new String(inBytes, "UTF-8");
>>>>>>>      } catch ( UnsupportedEncodingException e ) {
>>>>>>>          sneakyThrow(e);
>>>>>>>      }
>>>>>>>  }
>>>>>>> };
>>>>>>>
>>>>>>>
>>>>>>> NB: For sanity purposes, the restriction on javac that the try  
>>>>>>> block
>>>>>>> MUST contain at least 1 statement that could throw a checked
>>>>>>> exception
>>>>>>> type that is listed on one of the accompanying catch blocks  
>>>>>>> (other
>>>>>>> than Exception, which you can always catch), should go away;  
>>>>>>> this has
>>>>>>> already been proposed before, and in fact is a backwards
>>>>>>> compatibility
>>>>>>> painpoint for the coin proposal that allows you to rethrow a  
>>>>>>> final
>>>>>>> Exception as if its type is the intersection of all checked  
>>>>>>> types
>>>>>>> thrown by the statements in the try body.
>>>>>>>
>>>>>>> --Reinier Zwitserloot
>>>>>>> Like it? Tip it!
>>>>>>> http://tipit.to
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On May 20, 2009, at 02:22, Mark Mahieu wrote:
>>>>>>>
>>>>>>> Yeah, I suppose I did make it rather more complicated than it  
>>>>>>> needed
>>>>>>>
>>>>>>>> to be.
>>>>>>>>
>>>>>>>> Thanks for the feedback.
>>>>>>>>
>>>>>>>> Mark
>>>>>>>>
>>>>>>>>
>>>>>>>> On 19 May 2009, at 23:59, Joe Darcy wrote:
>>>>>>>>
>>>>>>>> Hello.
>>>>>>>>
>>>>>>>>> Hmm; this strikes me as a bit involved for the potential  
>>>>>>>>> benefits
>>>>>>>>> of the feature.
>>>>>>>>>
>>>>>>>>> -Joe
>>>>>>>>>
>>>>>>>>> On 03/30/09 05:04 PM, Mark Mahieu wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> Forgot my JLS references in the first one...
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> HTML version + prototype available at:
>>>>>>>>>>
>>>>>>>>>> http://slm888.com
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Rethrows Clause
>>>>>>>>>> v0.1.1
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> AUTHOR(S):
>>>>>>>>>>
>>>>>>>>>> Mark Mahieu
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> OVERVIEW
>>>>>>>>>>
>>>>>>>>>> FEATURE SUMMARY: Should be suitable as a summary in a  
>>>>>>>>>> language
>>>>>>>>>> tutorial.
>>>>>>>>>>
>>>>>>>>>> A new clause on method declarations which allows exception
>>>>>>>>>> translations (wrapping and rethrowing as a different type)  
>>>>>>>>>> to be
>>>>>>>>>> cleanly defined separately from the body of the method.  In  
>>>>>>>>>> many
>>>>>>>>>> cases, checked exception type names do not then need to be
>>>>>>>>>> repeated  in a method's throws clause and in a throw  
>>>>>>>>>> statement in
>>>>>>>>>> the method body.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> MAJOR ADVANTAGE: What makes the proposal a favorable change?
>>>>>>>>>>
>>>>>>>>>> The proposal adds direct support for a common idiom in  
>>>>>>>>>> daily use
>>>>>>>>>> by  Java programmers worldwide, allowing them to express  
>>>>>>>>>> their
>>>>>>>>>> intentions  with greater clarity and ease.  In comparison  
>>>>>>>>>> with
>>>>>>>>>> some proposals,  this is an attempt to make dealing with  
>>>>>>>>>> checked
>>>>>>>>>> exceptions easier by  increasing the expressiveness of  
>>>>>>>>>> exception
>>>>>>>>>> handling code in general,  rather than by attempting to  
>>>>>>>>>> deprecate
>>>>>>>>>> checked exceptions in favour  of unchecked exceptions.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> MAJOR BENEFIT: Why is the platform better if the proposal is
>>>>>>>>>> adopted?
>>>>>>>>>>
>>>>>>>>>> There is a reduction in the amount of boilerplate Java
>>>>>>>>>> programmers  have to read and write for code dealing with  
>>>>>>>>>> checked
>>>>>>>>>> exceptions.   Declarations specifying both thrown and  
>>>>>>>>>> rethrown
>>>>>>>>>> (wrapped) exceptions  are kept together, aiding  
>>>>>>>>>> comprehension of
>>>>>>>>>> the code.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> MAJOR DISADVANTAGE: There is always a cost.
>>>>>>>>>>
>>>>>>>>>> As with any syntax sugar which enables an alternative way of
>>>>>>>>>> expressing an existing idiom, programmers may be tempted to  
>>>>>>>>>> use
>>>>>>>>>> it  even when the existing idiom would be more appropriate.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> ALTERNATIVES: Can the benefits and advantages be had some way
>>>>>>>>>> without  a language change?
>>>>>>>>>>
>>>>>>>>>> No.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> EXAMPLES
>>>>>>>>>>
>>>>>>>>>> SIMPLE EXAMPLE: Show the simplest possible program  
>>>>>>>>>> utilizing the
>>>>>>>>>> new  feature.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Before:
>>>>>>>>>>
>>>>>>>>>> void before() throws ConfigException {
>>>>>>>>>>     try {
>>>>>>>>>>         Class.forName("where.is.the.Code");
>>>>>>>>>>     }
>>>>>>>>>>     catch (ClassNotFoundException e) {
>>>>>>>>>>         throw new ConfigException(e);
>>>>>>>>>>     }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> After:
>>>>>>>>>>
>>>>>>>>>> void after()
>>>>>>>>>>     catch ClassNotFoundException throw ConfigException {
>>>>>>>>>>
>>>>>>>>>>     Class.forName("here.is.the.Code");
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> ADVANCED EXAMPLE: Show advanced usage(s) of the feature.
>>>>>>>>>>
>>>>>>>>>> Before:
>>>>>>>>>>
>>>>>>>>>> void suspendAccount()
>>>>>>>>>>     throws AuthorizationException,
>>>>>>>>>>            PersistenceException {
>>>>>>>>>>     try {
>>>>>>>>>>         checkMyAuthoritah();
>>>>>>>>>>         db.update(/*...*/);
>>>>>>>>>>         log.recordInfo(/*...*/);
>>>>>>>>>>     }
>>>>>>>>>>     catch (InfernalDBException e) {
>>>>>>>>>>         throw new PersistenceException(e);
>>>>>>>>>>     }
>>>>>>>>>>     catch (InfernalLogException e) {
>>>>>>>>>>         throw new RuntimeException(e);
>>>>>>>>>>     }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> After:
>>>>>>>>>>
>>>>>>>>>> void suspendAccount()
>>>>>>>>>>     throws AuthorizationException
>>>>>>>>>>     catch InfernalDBException throw PersistenceException,
>>>>>>>>>>           InfernalLogException throw RuntimeException {
>>>>>>>>>>
>>>>>>>>>>     checkMyAuthoritah();
>>>>>>>>>>     db.update(/*...*/);
>>>>>>>>>>     log.recordInfo(/*...*/);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> DETAILS
>>>>>>>>>>
>>>>>>>>>> SPECIFICATION: Describe how the proposal affects the grammar,
>>>>>>>>>> type  system, and meaning of expressions and statements in  
>>>>>>>>>> the
>>>>>>>>>> Java  Programming Language as well as any other known  
>>>>>>>>>> impacts.
>>>>>>>>>>
>>>>>>>>>> The syntactic grammar is modified to allow an optional  
>>>>>>>>>> rethrows
>>>>>>>>>> clause immediately prior to a MethodBody:
>>>>>>>>>>
>>>>>>>>>>     MethodDeclaratorRest:
>>>>>>>>>>              FormalParameters {[]} [throws
>>>>>>>>>> QualifiedIdentifierList] ( ( [catch ExceptionTranslationList]
>>>>>>>>>> MethodBody ) | ; )
>>>>>>>>>>
>>>>>>>>>>     ExceptionTranslationList:
>>>>>>>>>>              QualifiedIdentifier throw QualifiedIdentifier
>>>>>>>>>> { ,  ExceptionTranslationList }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> JLSv3 ї8.4.6 : A rethrows clause lists one or more exception
>>>>>>>>>> translations, each translation consisting of a caught type  
>>>>>>>>>> C and
>>>>>>>>>> a  translated type T for which all of the following must  
>>>>>>>>>> hold:
>>>>>>>>>> * C <: java.lang.Exception
>>>>>>>>>> * T < java.lang.Throwable
>>>>>>>>>> * Neither C nor T is a type variable.
>>>>>>>>>> * T has an accessible constructor suitable for rethrowing a
>>>>>>>>>> value of type C (see below).
>>>>>>>>>> * T is not the same type as C.
>>>>>>>>>>
>>>>>>>>>> Any exceptions thrown by the method body which are a  
>>>>>>>>>> subtype of a
>>>>>>>>>> caught exception type in the rethrows clause, are rethrown  
>>>>>>>>>> as the
>>>>>>>>>> corresponding translated exception type.
>>>>>>>>>>
>>>>>>>>>> For a given translated type T with corresponding caught  
>>>>>>>>>> type C, if
>>>>>>>>>> T  has an accessible constructor accepting a value of type  
>>>>>>>>>> C, then
>>>>>>>>>> the  translation is equivalent to the following:
>>>>>>>>>>
>>>>>>>>>> catch (C e) {
>>>>>>>>>>     throw new T(e);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> Otherwise it must have an accessible no argument  
>>>>>>>>>> constructor, and
>>>>>>>>>> the  translation is equivalent to:
>>>>>>>>>>
>>>>>>>>>> catch (C e) {
>>>>>>>>>>     throw new T().initCause(e);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> A rethrows clause does not restrict which types may appear  
>>>>>>>>>> in a
>>>>>>>>>> throws clause for the same method.  In particular, for a  
>>>>>>>>>> given
>>>>>>>>>> caught  type C in the rethrows clause, it is permitted for  
>>>>>>>>>> some
>>>>>>>>>> type C1 :> C  to also be listed in the throws clause.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> JLSv3 ї8.2 : The set of exception types declared to be  
>>>>>>>>>> thrown by
>>>>>>>>>> a  method is the union of:
>>>>>>>>>> * the types in the throws clause
>>>>>>>>>> * the translated types in the rethrow clause
>>>>>>>>>> * the types thrown by the translated types' selected
>>>>>>>>>> constructors
>>>>>>>>>>
>>>>>>>>>> JLSv3 ї11.2.2 : For the purposes of exception analysis, the  
>>>>>>>>>> set
>>>>>>>>>> of  checked exception types which may be thrown by the  
>>>>>>>>>> method's
>>>>>>>>>> body is  the union of:
>>>>>>>>>> * the types in the throws clause
>>>>>>>>>> * the caught types in the rethrows clause
>>>>>>>>>>
>>>>>>>>>> JLSv3 ї11.2.3 : It is a compile-time error if a rethrows  
>>>>>>>>>> clause
>>>>>>>>>> contains a translation from a checked exception type C but  
>>>>>>>>>> there
>>>>>>>>>> exists no checked exception type E such that all of the  
>>>>>>>>>> following
>>>>>>>>>> hold:
>>>>>>>>>> * E <: C
>>>>>>>>>> * The method body can throw E
>>>>>>>>>> * No preceding translation in the rethrow clause catches E or
>>>>>>>>>> a  supertype of E
>>>>>>>>>> unless C is the class java.lang.Exception.
>>>>>>>>>>
>>>>>>>>>> JLSv3 ї13.4.21 : Changes to the rethrows clause of methods  
>>>>>>>>>> or
>>>>>>>>>> constructors do not break compatibility with existing  
>>>>>>>>>> binaries;
>>>>>>>>>> these  clauses are checked only at compile time.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> COMPILATION: How would the feature be compiled to class  
>>>>>>>>>> files?
>>>>>>>>>> Show  how the simple and advanced examples would be compiled.
>>>>>>>>>> Compilation  can be expressed as at least one of a  
>>>>>>>>>> desugaring to
>>>>>>>>>> existing source  constructs and a translation down to  
>>>>>>>>>> bytecode. If
>>>>>>>>>> a new bytecode is  used or the semantics of an existing  
>>>>>>>>>> bytecode
>>>>>>>>>> are changed, describe  those changes, including how they  
>>>>>>>>>> impact
>>>>>>>>>> verification. Also discuss  any new class file attributes  
>>>>>>>>>> that are
>>>>>>>>>> introduced. Note that there  are many downstream tools that
>>>>>>>>>> consume class files and that they may  to be updated to  
>>>>>>>>>> support
>>>>>>>>>> the proposal!
>>>>>>>>>>
>>>>>>>>>> A simple desugaring could consist of enclosing the method  
>>>>>>>>>> body's
>>>>>>>>>> statements in a try statement, with catch clauses for each
>>>>>>>>>> translated  exception type.  For example, the following  
>>>>>>>>>> method:
>>>>>>>>>>
>>>>>>>>>> Method findMethod()
>>>>>>>>>>     catch ClassNotFoundException throw ConfigException,
>>>>>>>>>>           NoSuchMethodException throw ConfigException {
>>>>>>>>>>
>>>>>>>>>>     Class<?> c = Class.forName("some.Thing");
>>>>>>>>>>     return c.getDeclaredMethod("execute", null);
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> would be desugared to:
>>>>>>>>>>
>>>>>>>>>> Method findMethod()
>>>>>>>>>>     throws ConfigException {
>>>>>>>>>>     try {
>>>>>>>>>>         Class<?> c = Class.forName("some.Thing");
>>>>>>>>>>         return c.getDeclaredMethod("execute", null);
>>>>>>>>>>     }
>>>>>>>>>>     catch (ClassNotFoundException e) {
>>>>>>>>>>         throw new ConfigException(e);
>>>>>>>>>>     }
>>>>>>>>>>     catch (MethodNotFoundException e) {
>>>>>>>>>>         throw new ConfigException(e);
>>>>>>>>>>     }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> No changes to the classfile format are required.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> TESTING: How can the feature be tested?
>>>>>>>>>>
>>>>>>>>>> An initial set of jtreg tests is included in the prototype.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> LIBRARY SUPPORT: Are any supporting libraries needed for the
>>>>>>>>>> feature?
>>>>>>>>>>
>>>>>>>>>> No
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> REFLECTIVE APIS: Do any of the various and sundry  
>>>>>>>>>> reflection APIs
>>>>>>>>>> need to be updated? This list of reflective APIs includes  
>>>>>>>>>> but is
>>>>>>>>>> not  limited to core reflection (java.lang.Class and
>>>>>>>>>> java.lang.reflect.*),  javax.lang.model.*, the doclet API,  
>>>>>>>>>> and
>>>>>>>>>> JPDA.
>>>>>>>>>>
>>>>>>>>>> com.sun.source.tree.MethodTree would require updates to  
>>>>>>>>>> access
>>>>>>>>>> the  rethrows clause's caught and translated types.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> OTHER CHANGES: Do any other parts of the platform need be  
>>>>>>>>>> updated
>>>>>>>>>> too? Possibilities include but are not limited to JNI,
>>>>>>>>>> serialization,  and output of the javadoc tool.
>>>>>>>>>>
>>>>>>>>>> No
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> MIGRATION: Sketch how a code base could be converted,  
>>>>>>>>>> manually or
>>>>>>>>>> automatically, to use the new feature.
>>>>>>>>>>
>>>>>>>>>> Catch clauses which simply wrap and rethrow an exception as
>>>>>>>>>> another  exception type not caught in an enclosing scope,  
>>>>>>>>>> can be
>>>>>>>>>> trivially  replaced with a rethrows clause, either manually  
>>>>>>>>>> or
>>>>>>>>>> automatically.
>>>>>>>>>>
>>>>>>>>>> It should be possible for tools to offer bidirectional
>>>>>>>>>> conversions  such that an exception translation may be  
>>>>>>>>>> moved back
>>>>>>>>>> into the method  body if it is subsequently decided that
>>>>>>>>>> additional logic is required.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> COMPATIBILITY
>>>>>>>>>>
>>>>>>>>>> BREAKING CHANGES: Are any previously valid programs now  
>>>>>>>>>> invalid?
>>>>>>>>>> If  so, list one.
>>>>>>>>>>
>>>>>>>>>> No
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> EXISTING PROGRAMS: How do source and class files of earlier
>>>>>>>>>> platform  versions interact with the feature? Can any new
>>>>>>>>>> overloadings occur?  Can any new overriding occur?
>>>>>>>>>>
>>>>>>>>>> The semantics of existing class files and legal source  
>>>>>>>>>> files are
>>>>>>>>>> unchanged by this feature.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> REFERENCES
>>>>>>>>>>
>>>>>>>>>> EXISTING BUGS: Please include a list of any existing Sun  
>>>>>>>>>> bug ids
>>>>>>>>>> related to this proposal.
>>>>>>>>>>
>>>>>>>>>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6534270
>>>>>>>>>> (similar, but emphasizes unchecked exceptions)
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> URL FOR PROTOTYPE (optional):
>>>>>>>>>>
>>>>>>>>>> http://slm888.com/javac
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>
>>
>




More information about the coin-dev mailing list