Reputation: 2625
The Oracle Java documentation for the then-new try-with-resources shows all examples with the AutoCloseable
in question being constructed in the try(...)
:
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
...
}
That seems secure in the sense that even if the constructor (as opposed to some subsequent event) already acquires resources that have to be released, they would be released if we ran into an exception.
However, as, e.g., https://www.baeldung.com/java-try-with-resources points out, starting with Java 9, we can use effectively final resources initialized before the try(...)
:
FileOutputStream fos = new FileOutputStream("output.txt");
try (fos) {
// do stuff
}
In a similar, pre-Java-9 vein, I have seen:
CustomStreamLikeThing connection = new CustomStreamLikeThing("db_like_thing");
// ... do some stuff that may throw a RuntimeException
try (CustomStreamLikeThing connection2=connection) {
// do some more stuff
}
Which seems legal, but at the same time to somewhat weaken the concept: If the CustomStreamLikeThing
acquires closeable resources, they may not be released.
Are there guidelines about when the constructor of an AutoCloseable
should be enclosed in the try(...)
, or is this a matter of taste (as in "enclose all those things that I think could throw an exception")?
Upvotes: 4
Views: 673
Reputation: 140641
Are there guidelines about when the constructor of an AutoCloseable should be enclosed in the
try(...)
, or is this a matter of taste (as in "enclose all those things that I think could throw an exception")?
The basic rule in coding is: you do anything on purpose. Which requires that you understand what you are doing, and what each "thing" in your code is doing.
Meaning: if you have something that needs to be "closed", then the common sense rule is: use try with resources.
But you are asking specifically about the subtle aspect of the "object creation". Should that go into the try()
, or can it be done before?!
For that: no hard rules, as long as your source code does semantically the right thing. Therefore, it boils down to these two aspects:
try(fos)
option works with java9. For people that don't have to patch older products, that is fine. But for people that regularly have to patch "the same change" to different versions of the same product ... those people might prefer source code that can just be merged into a java8 environment.Upvotes: 1
Reputation: 159270
You should always use try-with-resources if you can.
When you allocate a resource, you should make sure the resource is always released when you're done with it. That "always" generally means that you should use a try-finally
, so that any error occurring after the resource is allocate won't prevent the resource from being released.
Which means that if you don't use try-with-resources, you need to use try-finally
, so why did I say to always use try-with-resources?
Because calling close()
can fail too, and try-with-resources handles that for you, with a feature added when try-with-resources was added, called suppressed exceptions.
You'll know the importance of this if you've ever cursed at a stacktrace of a failed close()
call, that has replaced the exception thrown by the code that should have been in a try-with-resources block, so you now don't know what actually caused the problem.
Suppressed exceptions is a commonly overlooked feature, but is a great time saver when a cascaded exception is thrown by a close
method. For that alone, always use try-with-resources.
Side benefit: Try-with-resources is less code than using try-finally
, and you should definitely use one of those two when handling resources.
Upvotes: 2
Reputation: 19910
When having the constructor directly in the try-with-resources statement like this:
try (MyCloseable c = new MyCloseable()) {
// do stuff with c
} catch (IOException e) {
// handle exception
}
then the catch block will be called when any of the following throw an exception:
close()
-method.So you can't have a catch block for every special case when e.g. all throw an IOException
.
Whereas with code like this, you can control the exception handling a bit more:
MyCloseable c;
try {
c = new MyCloseable();
} catch (IOException e) {
// handle constructor exception
}
try (c) {
// do stuff with c
} catch (IOException e) {
// handle exception from close() or try-body
}
But then again, you still have a single catch block for the try-body and the close method in the above example, and doing something like this would not be recommended:
// initialize c like in the second example
try (c) {
try {
// do stuff with c
} catch (IOException e) {
// handle try-body exception
}
} catch (IOException e) {
// handle close() exception
}
Upvotes: 1
Reputation: 152
The pre-java 9 is just hack to allow autoclosable to work on the underlying object and close and release resources.
With java 9, it's more embedded in the language itself to allow closing of resources initialized outside the try with block.
In terms of preference, it's more about whether its elegant by the time you finish the try with block, if it becomes too huge, might as well move it out.
Upvotes: 0