You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@harmony.apache.org by Jesse Wilson <je...@google.com> on 2009/11/13 21:24:06 UTC

SneakyThrow!

Harmony team,

In the process of fixing a bug, I just checked in
SneakyThrow<http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/SneakyThrow.java?revision=835972&view=markup>.
This is a weird API that probably won't see much use. But it
*is*interesting and something you might want to take a look at. With
regular
code, coping with exceptions correctly is pain. Consider the full body of
BufferedWriter.close(), in particular the last 10 lines.

     public void close() throws IOException {
        synchronized (lock) {
            if (isClosed()) {
                return;
            }

            Throwable thrown = null;
            try {
                flushInternal();
            } catch (Throwable e) {
                thrown = e;
            }
            buf = null;

            try {
                out.close();
            } catch (Throwable e) {
                if (thrown == null) {
                    thrown = e;
                }
            }
            out = null;

            if (thrown != null) {
                if (thrown instanceof IOException) {
                    throw (IOException) thrown;
                } else if (thrown instanceof RuntimeException) {
                    throw (RuntimeException) thrown;
                } else if (thrown instanceof Error) {
                    throw (Error) thrown;
                } else {
                    throw new AssertionError();
                }
            }
        }
    }

This new API shortens that last section dramatically:

        if (thrown != null) {
            SneakyThrow.sneakyThrow(thrown);
        }

This comes up more often in library code, where it's necessary to cleanly
close resources without losing exceptions. SneakyThrow increases the chances
that we'll do the right thing when it comes to exception handling. Please
don't use it, unless you really, really need to!

Cheers,
Jesse

PS - SneakyThrow is based on puzzler #43 in the fine Java Puzzlers book.

Re: SneakyThrow!

Posted by Nathan Beyer <nd...@apache.org>.
On Tue, Nov 17, 2009 at 10:33 AM, Jesse Wilson <je...@google.com> wrote:
> On Tue, Nov 17, 2009 at 7:56 AM, Tim Ellison <t....@gmail.com> wrote:
>
>> The difference I see, by just staring at the code and without running
>> it, is that if the 'thrown' Throwable that was caught earlier is not one
>> of the tested types (IOException | RuntimeException | Error) then the
>> original code throws an AssertionError, whereas sneaky will re-throw the
>> raised type.
>>
>> Not sure why the original has AssertionError...
>>
>
> Yeah, the repeated "throw" statements in that code obscured my intentions.
>
> "throw new AssertionError()" is idiomatic for lines of code that should

This idiom is still not well understood, which is why i generally put
a comment above any AssertionError throw "// should never happen".
That may just be my preventative habit for avoiding questions and
confusing looks.

I also suspect that the concept of 'assert' in any programming
language is not well understood.

-Nathan

> never ever be executed. In this case, IOException, RuntimeException and
> Error were the only things that could have been thrown by the try blocks.
>

Re: SneakyThrow!

Posted by Jesse Wilson <je...@google.com>.
On Tue, Nov 17, 2009 at 8:53 AM, Tim Ellison <t....@gmail.com> wrote:

> Now I've gone back and looked, they were never the real last 10 lines of
> the close() method.  More a case of, rather than code it like this ... I
> did it like this ..., right?
>

Yes, I named the wrong file. But for a few hours, FilterOutputStream
actually had the original code. The change also covers catching Throwable
rather than Exception, which means that things will be closed properly even
in the event of a StackOverflowError or similar.

Re: SneakyThrow!

Posted by Tim Ellison <t....@gmail.com>.
On 17/Nov/2009 16:33, Jesse Wilson wrote:
> On Tue, Nov 17, 2009 at 7:56 AM, Tim Ellison <t....@gmail.com> wrote:
> 
>> The difference I see, by just staring at the code and without running
>> it, is that if the 'thrown' Throwable that was caught earlier is not one
>> of the tested types (IOException | RuntimeException | Error) then the
>> original code throws an AssertionError, whereas sneaky will re-throw the
>> raised type.
>>
>> Not sure why the original has AssertionError...
> 
> Yeah, the repeated "throw" statements in that code obscured my intentions.
> 
> "throw new AssertionError()" is idiomatic for lines of code that should
> never ever be executed. In this case, IOException, RuntimeException and
> Error were the only things that could have been thrown by the try blocks.

I see, you fooled me in your original note with:

> Consider the full body of BufferedWriter.close(), in particular the
> last 10 lines.

Now I've gone back and looked, they were never the real last 10 lines of
the close() method.  More a case of, rather than code it like this ... I
did it like this ..., right?

Regards,
Tim

Re: SneakyThrow!

Posted by Jesse Wilson <je...@google.com>.
On Tue, Nov 17, 2009 at 7:56 AM, Tim Ellison <t....@gmail.com> wrote:

> The difference I see, by just staring at the code and without running
> it, is that if the 'thrown' Throwable that was caught earlier is not one
> of the tested types (IOException | RuntimeException | Error) then the
> original code throws an AssertionError, whereas sneaky will re-throw the
> raised type.
>
> Not sure why the original has AssertionError...
>

Yeah, the repeated "throw" statements in that code obscured my intentions.

"throw new AssertionError()" is idiomatic for lines of code that should
never ever be executed. In this case, IOException, RuntimeException and
Error were the only things that could have been thrown by the try blocks.

Re: SneakyThrow!

Posted by Tim Ellison <t....@gmail.com>.
On 13/Nov/2009 20:24, Jesse Wilson wrote:
> Harmony team,
> 
> In the process of fixing a bug, I just checked in
> SneakyThrow<http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/SneakyThrow.java?revision=835972&view=markup>.
> This is a weird API that probably won't see much use. But it
> *is*interesting and something you might want to take a look at. With
> regular
> code, coping with exceptions correctly is pain. Consider the full body of
> BufferedWriter.close(), in particular the last 10 lines.
> 
>      public void close() throws IOException {
>         synchronized (lock) {
>             if (isClosed()) {
>                 return;
>             }
> 
>             Throwable thrown = null;
>             try {
>                 flushInternal();
>             } catch (Throwable e) {
>                 thrown = e;
>             }
>             buf = null;
> 
>             try {
>                 out.close();
>             } catch (Throwable e) {
>                 if (thrown == null) {
>                     thrown = e;
>                 }
>             }
>             out = null;
> 
>             if (thrown != null) {
>                 if (thrown instanceof IOException) {
>                     throw (IOException) thrown;
>                 } else if (thrown instanceof RuntimeException) {
>                     throw (RuntimeException) thrown;
>                 } else if (thrown instanceof Error) {
>                     throw (Error) thrown;
>                 } else {
>                     throw new AssertionError();
>                 }
>             }
>         }
>     }
> 
> This new API shortens that last section dramatically:
> 
>         if (thrown != null) {
>             SneakyThrow.sneakyThrow(thrown);
>         }

The difference I see, by just staring at the code and without running
it, is that if the 'thrown' Throwable that was caught earlier is not one
of the tested types (IOException | RuntimeException | Error) then the
original code throws an AssertionError, whereas sneaky will re-throw the
raised type.

Not sure why the original has AssertionError...

> This comes up more often in library code, where it's necessary to cleanly
> close resources without losing exceptions. SneakyThrow increases the chances
> that we'll do the right thing when it comes to exception handling. Please
> don't use it, unless you really, really need to!
> 
> Cheers,
> Jesse
> 
> PS - SneakyThrow is based on puzzler #43 in the fine Java Puzzlers book.

Aww, I thought you'd at least have posted an affiliate link too?! <g>

Regards,
Tim