8

I like using final variables whenever possible. Often those variables have to be closed afterwards. Note that I'm currently working on Java 6 so there is not closeable interface, but the same applies to later Java versions as well. For example:

final InputStream in;

try{
    in=new InputStream(...);
    //...
}catch(...) {}
finally{
    in.close(); //whoops!
}

At that point the IDE will point out that in might be uninitialized. Wrapping the close statement in an if like so

if(in!=null)
    in.close();

doesn't work either, despite null being the default value for uninitialized objects. My solution so far is to forget about declaring them final in the first place, and initializing them as null at declaration.

Another solution is to place the declaration and close statement in the try block:

try{
    final InputStream in=new InputStream(...);
    //...
    in.close();
}catch(InputStreamRelatedException) {};

which would leave in open (?) in case of non-InputStream related exception.

What is the preferred method?

rath
  • 886

2 Answers2

15

You are running into difficulties because you are trying to handle two different cases with the same try-catch:

  • constructing the InputStream could fail, in which case you also can't close() anything.
  • using the InputStream could fail, in which case you need a close().

The solution is to separate these cases, and not instantiate the object in the same scope where it might be used:

final InputStream in;
try { in = new InputStream(); }
catch (...) { ... /* must throw or return, or assign to `in` */ }

try { doSomethingWith(in); }
catch (...) { ... }
finally { in.close(); }
amon
  • 135,795
1

I agree with amon that there are two separate concerns but disagree with him/her over what those concerns are. By choosing a slightly different set of concerns you can write terser code. Also, amon's code block throws an IOException because in.close()'s IOException is not caught but my code does not so long as you catch the IOException in the catch block.

IMO the two concerns are:

  • an I/O error occurring at any point (in construction or use of the stream)
  • closing the stream

With that in mind, we can write code like:

try {
  final InputStream in = ... // assign in
  try {
    // use in
  } finally {
    in.close();
  }
} catch (...) {} // if you catch IOException here, the block throws no IOException

Note that ideally you would put an additional try/catch around in.close() to stop any exception thrown there from superseding the exception in the outer block. But that is horribly verbose.

As an aside, please do not use the anti-pattern of assigning null to in and checking for null in the finally block. It complicates the code for no good reason.