Use resource in try with resource statement that was created before

16.1k views Asked by At

Since Java 7, we can use try with resources:

try (One one = new One(); Two two = new Two()) {
    System.out.println("try");
} catch (Exception ex) { ... }

Now my question is, why do I have to create the object in the try-statement? Why am I not allowed to create the object before the statement like this:

One one = new One();
try (one; Two two = new Two()) {
    System.out.println("try");
} catch (Exception ex) { ... }

I don't see any reasons, why this should be a problem. Though I get the error message "Resource references are not supported at this language level". I set my IDE (IntelliJ IDEA) to Java 8, so that should work. Is there a good reason for this, being not allowed?

3

There are 3 answers

3
teppic On BEST ANSWER

You don't have to create the object in the try-with-resources statement, you just have to declare some local variables of a type that implements AutoCloseable. The variables are effectively final, and scoped to the try block, which allows the compiler to use them to generate the close boilerplate needed for cleanup.

FileInputStream f1 = new FileInputStream("test1.xml");
FileInputStream f2 = new FileInputStream("test2.xml");
// Don't need to create the resources here, just need to declare some vars
try (InputStream in1 = f1; InputStream in2 = f2) {
    // error; in1 is final
    in1 = new FileInputStream("t");
}

Better Resource Management with Java SE 7: Beyond Syntactic Sugar.

Addendum: Since java 9 the requirements have been relaxed; you don't have to redeclare the variables in the try block if the originals are effectively final.

JEP 213

0
Mark Rotteveel On

It is actually possible:

One one = new One();
try (One temp = one; ....;) {

}

and starting with Java 9 you don't even need to declare an additional variable, and instead you can use the variable directly:

One one = new One();
try (one) {
    //...
}

However there is almost never a good reason to create the resource before the try-with-resources. This was probably the reason the try-with-resources block originally required you to declare a new variable in the resource-list (which also easily enforces that the variable is final). However, the language designers decided that flexibility was more important here.

Creating the resource before the try-with-resources block could lead to subtle bugs because the resource is not properly closed if an exception happens before you enter the block (eg if you do other things between creating a One and entering the try-with-resources block).

And generally you should have no reason for accessing a resource after it has been closed, so you should limit the scope to the time the resource is open (ie the try-with-resources block). If you do need to access a resource after it has been closed, you may need to consider a different design, where the (closable) resource is separated from the object/data you need after closing the resource, or you need to use nested try-with-resources blocks.

An exception to this, might be if you get an AutoCloseable passed in, and your method must guarantee it is closed on exit, but this is generally a design smell though: the one opening a resource should also be responsible for closing it.

1
Harry Cho On

Starting from Java 9, try-resource block with final references can be cleaned up without redeclaring variable in the try block.

For example,

final One one = new One();
try (one) {
    System.out.println("try");
} catch (Exception ex) { ... }

Source:

Allow effectively-final variables to be used as resources in the try-with-resources statement. The final version of try-with-resources statement in Java SE 7 requires a fresh variable to be declared for each resource being managed by the statement. This was a change from earlier iterations of the feature. The public review draft of JSR 334 discusses the rationale for the change from the early draft review version of try-with-resource which allowed an expression managed by the statement. The JSR 334 expert group was in favor of an additional refinement of try-with-resources: if the resource is referenced by a final or effectively final variable, a try-with-resources statement can manage the resource without a new variable being declared. This restricted expression being managed by a try-with-resources statement avoids the semantic issues which motivated removing general expression support. At the time the expert group settled on this refinement, there was insufficient time in the release schedule to accommodate the change.