First of all, for the sake of a little sanity check, I would say, that I could not find the example, you provided, in Joshua Bloch's Effective Java (3rd Edition, 2018). If you are reading the previous version, it should be better to get the latest one. If it is my bad, please refer particular page number to me.
Now with respect to the question itself.
Let's begin with JSL §14.20.3, which says, that Resources are separated by ;
. This means, that disregarding of how long our decoration chain of the object creations will be (e.g. new Obj1(new Obj2(...new ObjK(..)));
it will be treated as a one single resource, as it is a one definition/statement.
As we now know what does a Single Resource constitute, let me shed some light from my observation.
Reasons, why it is better to define resources separately
- JSL §14.20.3 also states, that:
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.
Q: What does that mean for us?
A: If that single resource is a chain of objects passed into wrapping constructors, it does not mean, that throwing an exception from the initialization phase will close those embedded resources, rather the .close()
method will be invoked on the parent (wrapper, enclosing) objects, and therefore, you might easily end up with a resource leak.
On the other hand, you can be resting assured that all resources will be closed, if you have defined them separately.
- JSL §14.20.3 also states, that:
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.
Q: What does that mean for us?
A: If you have declared your resources separately, then it does not matter which throws exception and which does not. They all will be closed successfully.
- Unfortunately the book you mentioned (at least the 3rd version it) does not cover the question you brought up here; however, I did some more research and found that Core Java (10th Edition), by Cay S. Horstmann, confirms the point I referred to above, in its §7.2.5:
When the block exits normally, or when there was an exception, the in.close()
method is called, exactly as if you had used a finally block.
and
No matter how the block exits, both in
and out
are closed.
in
and out
are Autoclosable objects in the examples given in the book.
Q: What does this mean?
A: It means, that exceptions thrown from any phase of one of the resources, does not anyhow affect how another resources are closed.
Based on all above, I think, that it also depends on how the resources are implemented. E.g. if the enclosing resource, upon an invocation of its .close()
, implements the logic to close the enclosed resource, then your concern:
I guess it might happen that new Impl1() performs fine, but new Impl2() crashes, and in this case Java would have no reference to the Impl1, in order to close it.
will not really be a problem in your particular case, as the container resource which is being closed, will hold a reference to the contained resource and eventually will close it (or will just implement its closure);
However, it is still a bad idea to construct resources in the decoration of chaining them into wrapper constructors, due to several reasons I brought up.
P. S.
In any case, disregarding of how the resources are implemented or how you chain them or etc. everything, including JLS, Core Java book, OCP Java SE 8 book, and points brought here, indicate that it's always best to declare your resources separately.
BufferedWriter
will fail, unless, say, the passed writer is null. – Scopclose()
methods also close the inner resources. Again, this depends on the specific resources, since some (few? many? most?) will handle close idempotently. – Scop