javajava-9try-with-resourcesautocloseable

Is try-with-resource not safe when declaring multiple effectively final resources?


Since Java 9 we can use effectively final variables in try-with-resources. (See JEP 213.)

The example below presents a situation where one of the resources initialization throws an exception.

    public static void main(String[] args) {
        Resource1 r1 = new Resource1();
        Resource2 r2 = new Resource2(); // exception will be thrown
        try (r1; r2) {
            System.out.println("TryWithResources.main() try");
        } catch (Exception e) {
            System.out.println("TryWithResources.main() catch");
        }
    }
    
    static class Resource1 implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("TryWithResources.Resource1.close()");
        }
    }
    
    static class Resource2 implements AutoCloseable {
        public Resource2() {
            throw new RuntimeException();
        }
        @Override
        public void close() throws Exception {
            System.out.println("TryWithResources.Resource2.close()");
        }
    }

When I run this example, the only output I get is a RuntimeException, meaning that Resource1 was not closed. That was expected, since it wasn't initialized in the try-with-resources.

But, is this the expected outcome, or am I missing anything?

Because, if this is actually the way it is supposed to work, then It seems to me that this new syntax actually removes a lot of the safety that was brought originally by the try-with-resources statement.

Can someone please confirm if that really is the case? And, if positive, why would we ever use this syntax with multiple resources and take that risk?


Solution

  • I think you're assuming that the "initialization" occurs in the try statement. The exception is thrown by the constructor before the try-with-resources is reached. In other words, the line try (r1; r2) { in itself does not initialize the resources, it just refers to them as variables. It's different than initializing the resource in try blocks:

    try (r1; Resource2 r2 = new Resource2()) { // this causes r1 to close if r2 throws an exception
    

    Having said this, you're right with the point that the new syntax (accessing a final variable) gives flexibility at the expense of a possibility for failing to close previously created resources (as demonstrated in your case). Personally I never had a reason to use that new syntax. There is no good reason I can think of for not creating the resource within the try statement, after all there is no point of using a resource after it is closed.