Hans
Hans

Reputation: 2625

(When) Should try-with enclose the AutoCloseable constructor?

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

Answers (4)

GhostCat
GhostCat

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:

  • You talk to your team, and you agree on your "preferred" style, and then your whole team follows that. In other words: pick the style you prefer, and then stick to that: be consistent!
  • You look at your requirements. Your 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

Andreas
Andreas

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

Lino
Lino

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:

  • the constructor
  • the try-body
  • the 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

Soni
Soni

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

Related Questions