Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use resource in try with resource statement that was created before

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?

like image 834
Mathias Bader Avatar asked Dec 18 '16 08:12

Mathias Bader


People also ask

What is a resource in the try-with-resources statement?

The try -with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try -with-resources statement ensures that each resource is closed at the end of the statement.

Can multiple resources be used in try-with-resources?

We can declare multiple resources in a try block. Try initialization block can have any number of resources resulting in either null or non-null resources. In the below example, we can able to declare multiple resources in the try-with-resources statement.

Can we use try-with-resources without catch and finally?

Yes, It is possible to have a try block without a catch block by using a final block. As we know, a final block will always execute even there is an exception occurred in a try block, except System. exit() it will execute always.


2 Answers

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

like image 78
teppic Avatar answered Oct 19 '22 12:10

teppic


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.

like image 20
Mark Rotteveel Avatar answered Oct 19 '22 11:10

Mark Rotteveel