Is the close method on a try-with-resources idiom not called if a constructor throws an exception?
Asked Answered
T

3

12

I have a base class Base and a child class Child which extends it. Base implements java.lang.AutoCloseable.

Let's suppose that the constructor for Child throws a Foo.

Now consider

try (Base c = new Child()){
    /*Some code*/
} catch (final Foo e){
    /*Some more code*/
}

Is the Base#close method called if the exception is thrown? It is not on my machine, but is this something that the JLS has standardised?

Thereat answered 8/1, 2016 at 13:44 Comment(2)
If "the constructor for Child throws a Foo", no instance of Child exists in the calling scope, so close() cannot be called.Injector
If there is no object, there is no way close can be called on it.Ean
Q
16

Yes, close won't be called. This is specified in the JLS section 14.20.3:

Resources are initialized in left-to-right order. If a resource fails to initialize (that is, its initializer expression throws an exception), then all resources initialized so far by the try-with-resources statement are closed. If all resources initialize successfully, the try block executes as normal and then all non-null resources of the try-with-resources statement are closed.

Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.

In this case, an exception is thrown in the constructor so the resource is not initialized to a non-null value. Hence, the close method isn't called.

Quezada answered 8/1, 2016 at 13:51 Comment(0)
F
7

close will not be called. It wouldn't make sense to call it, since you don't have a fully-constructed object to close, and in similar calls, you might not have even entered a constructor:

try (Base b = makeBase()) {
    ...
}

where makeBase is

Base makeBase() {
    throw new RuntimeException();
}
Factory answered 8/1, 2016 at 13:49 Comment(3)
But the base class is fully constructed. That's what I'm battling with.Thereat
@P45Imminent: Java doesn't have any concept of base subobjects. There's no fully constructed "Base" object; the object isn't fully constructed until the Child constructor completes.Factory
Thanks for that. Upvoted but can't accept two answers sadly.Thereat
A
0

This may not be the best way to handle it but MAYBE you could delay throwing the exception until AFTER the object is constructed. How? You could have an exception attribute on the class and add a clause to the class's public methods that if that exception attribute is not null, you could then throw the cached exception that originated during object instantiation. This way, you should be able to reclaim the resources that might have been allocated during the partial construction of the object in question. I believe this is how these IO readers and writers classes handle these cases.

Again, this might not be the best way, but it might be something to consider if you REALLY need to.

Adventist answered 24/9, 2021 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.