review request for 7021209, use try-with-resources in lang, math, util

Stuart Marks stuart.marks at oracle.com
Tue Feb 22 07:34:33 UTC 2011


On 2/21/11 7:23 PM, Joe Darcy wrote:
> David Holmes wrote:
>> Hi Stuart,
>>
>> I'm unclear how try-with-resources applies in different cases. For example,
>> here in Package.java:
>>
>>       private static Manifest loadManifest(String fn) {
>>          try (FileInputStream fis = new FileInputStream(fn);
>>               JarInputStream jis = new JarInputStream(fis, false))
>>          {
>>              return jis.getManifest();
>>          } catch (IOException e) {
>>              return null;
>>          }
>>       }
>>
>> from which code will the the explicit catch block actually catch IOException?
>
> It will catch exceptions from jis.getManifest and from the compiler-generated
> close calls on fis and jis.

It will also catch exceptions from the construction of the FileInputStream and 
the JarInputStream.

>> Does this act as-if it were written:
>>
>>        try {
>>           try (FileInputStream fis = new FileInputStream(fn);
>>                JarInputStream jis = new JarInputStream(fis, false))
>>            {
>>              return jis.getManifest();
>>            }
>>         } catch (IOException e) {
>>             return null;
>>         }
>
> Yes it does.
>
> -Joe

Indeed, since the second block is the initial desugaring of the first, it's 
clear that the catch clause will also catch exceptions thrown from the 
construction of the resources.

David also asked:
> If so it seems a bit redundant to jump through all the suppressed exception stuff only to throw away any exception and return null.

True, some useless suppressed exception code will be generated. However, the 
logic deal with suppressed exceptions will only be executed if there is an 
exception thrown during construction or the jis.getManifest() call, *and* 
another exception is thrown from one of the close() calls on one of the resources.

The primary benefit of using TWR here is to ensure that the FileInputStream 
gets closed properly if construction or processing of the JarInputStream fails, 
which the original code did not do.

(There is also the benefit that the JarInputStream and the FileInputStream are 
both closed properly if getManifest() fails. But there is probably some 
redundancy here, since closing the JarInputStream will also probably close the 
underlying FileInputStream.)

It's an interesting exercise to write out the code with proper exception 
handling, without using TWR, disregarding any handling of suppressed 
exceptions, which we clearly don't care about in this case, and also 
disregarding closing of the JarInputStream. Here's what I came up with:

     private static Manifest loadManifest1(String fn) {
         try {
             Manifest man = null;
             FileInputStream fis = new FileInputStream(fn);
             try {
                 JarInputStream jis = new JarInputStream(fis, false);
                 man = jis.getManifest();
                 jis.close();
             } catch (IOException ioe1) {
                 // ignore
             } finally {
                 try {
                     fis.close();
                 } catch (IOException ioe2) {
                     // ignore
                 }
             }
             return man;
         } catch (FileNotFoundException fnfe) {
             return null;
         }
     }

Quite a mouthful, innit? Somebody could poke holes in this, but I'd guess that 
filling them would make it even more complex. I doubt that this could be 
simplified much if at all.

s'marks



More information about the core-libs-dev mailing list