How to Specify Multiple Resources in a Single Try-With-Resources Statement
The ability to specify multiple resources in a single try-with-resources statement is a feature introduced in Java 7. But, it can be fraught with peril if not used carefully. Read on to ensure you do this right every time.
Join the DZone community and get the full member experience.
Join For FreeOne of the more useful features of Java 7 was the introduction of the try-with-resources statement, also known as the Automatic Resource Management (ARM). The attractiveness of the try-with-resources statement lies in its promise to "ensure that each resource is closed at the end of the statement." A "resource" in this context is any class that implements AutoCloseable and its close() method and is instantiated inside the "try" clause of the try-with-resources statement.
The Java Language Specification (JLS) describes the try-with-resources statement in detail in Section 14.20.3 of Java SE 10 JLS in this case. The JLS states that the " try
-with-resources statement is parameterized with local variables (known as resources) that are initialized before execution of the try
block and closed automatically, in the reverse order from which they were initialized, after execution of the try
block."
The JLS clearly specifies that multiple resources can be defined in relation to a single try
-with-resources statement, and it specifies how multiple resources are specified. Specifically, it indicates that try
can be followed by a " ResourceSpecification" that is composed of a " ResourceList" that is composed of one or more "Resource's." When there is more than a single declared resource, the multiple resources are delimited by a semicolon (;
). This specification of multiple resources in a semicolon-delimited list is important because any candidate resources not declared in this manner will not be supported (will not be closed automatically) by the try
-with-resources statement.
The most likely source of errors when specifying multiple resources in a try
-with-resources statement is "nesting" instantiations of "resources," instead of explicitly instantiating local variables of each of them separately with semicolons between each instantiation. The examples below will illustrate the difference.
Two ridiculous but illustrative classes are shown next. Each class implements an AutoCloseable and can be used in conjunction with try
-with-resources and will have its close()
method called automatically when used correctly with the try
-with-resources statement. They are named to reflect that the OuterResource
can be instantiated with an instance of the InnerResource
.
InnerResource.java
package dustin.examples.exceptions;
import static java.lang.System.out;
public class InnerResource implements AutoCloseable
{
public InnerResource()
{
out.println("InnerResource created.");
}
public InnerResource(
final RuntimeException exceptionToThrow)
{
throw exceptionToThrow != null
? exceptionToThrow
: new RuntimeException("InnerResource: No exception provided.");
}
@Override
public void close() throws Exception
{
out.println("InnerResource closed.");
}
@Override
public String toString()
{
return "InnerResource";
}
}
OuterResource.java
package dustin.examples.exceptions;
import static java.lang.System.out;
public class OuterResource implements AutoCloseable
{
private final InnerResource wrappedInnerResource;
public OuterResource(final InnerResource newInnerResource)
{
out.println("OuterResource created.");
wrappedInnerResource = newInnerResource;
}
public OuterResource(
final InnerResource newInnerResource,
final RuntimeException exceptionToThrow)
{
wrappedInnerResource = newInnerResource;
throw exceptionToThrow != null
? exceptionToThrow
: new RuntimeException("OuterResource: No exception provided.");
}
@Override
public void close() throws Exception
{
out.println("OuterResource closed.");
}
@Override
public String toString()
{
return "OuterResource";
}
}
The two classes defined can now be used to demonstrate the difference between correctly declaring instances of each in the same try
-with-resources statement in a semicolon-delimited list and incorrectly nesting instantiation of the inner resource within the constructor of the outer resource. The latter approach doesn't work as well as hoped, because the inner resource —without a locally defined variable — is not treated as a "resource" in terms of invoking its AutoCloseable.close()
method.
The next code listing demonstrates the incorrect approach for instantiating "resources" in the try
-with-resources statement.
Incorrect Approach for Instantiating Resources in try
-with-resources Statement
try (OuterResource outer = new OuterResource(
new InnerResource(), new RuntimeException("OUTER")))
{
out.println(outer);
}
catch (Exception exception)
{
out.println("ERROR: " + exception);
}
When the code above is executed, the output " InnerResource created
" is seen, but no output is ever presented related to the resource's closure. This is because the instance of InnerResource
was instantiated within the call to the constructor of the OuterResource
class and was never assigned to its own separate variable in the resource list of the try
-with-resource statement. With a real resource, the implication of this is that the resource is not closed properly.
The next code listing demonstrates the correct approach for instantiating "resources" in the try
-with-resources statement.
Correct Approach for Instantiating Resources in try
-with-resources Statement
try(InnerResource inner = new InnerResource();
OuterResource outer = new OuterResource(inner, new RuntimeException("OUTER")))
{
out.println(outer);
}
catch (Exception exception)
{
out.println("ERROR: " + exception);
}
When the code above is executed, the output includes both InnerResource created
and InnerResource closed
, because the InnerResource
instance was properly assigned to a variable within the try
-with-resources statement. Therefore, its close()
method is properly called, even when an exception occurs during its instantiation.
The try-with-resources statement section of the Java Tutorials includes examples of correctly specifying the resources in the try
-with-resources as semicolon-delimited individual variable definitions. One example shows this correct approach with java.util.zip.ZipFile and java.io.BufferedWriter. Another example shows this correct approach with instances of java.sql.Statement and java.sql.ResultSet.
The introduction of try
-with-resources in JDK 7 was a welcome addition to the language that it made it easier for Java developers to write resource-safe applications that were not as likely to leak or waste resources. However, when multiple resources are declared within a single try
-with-resources statement, it's important to ensure that each resource is individually instantiated and assigned to its own variable declared within the try
's resource specifier list to ensure that each and every resource is properly closed. A quick way to check this is to ensure that for n, AutoCloseable
is implementing resources specified in the try.
There should be n-1 semicolons, separating those instantiated resources.
Published at DZone with permission of Dustin Marx, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments