Summary
I have a closeable type, CloseableClass
that can throw an IOError in its constructor, methods and maybe even inside close
. I want to use try-with-resources and still deal with errors during construction differently to errors during use (use includes cleanup). Even better, I would like to write maintainable code.
Let's say you wish to construct a closeable class instance and use it with a try-with-resources statement. It can throw IOException
in both its constructor and the method used in the body of the try-with-resources:
import java.io.Closeable;
import java.io.IOException;
import java.util.Random;
public class CloseableClass implements Closeable {
public CloseableClass() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public void internetStuff() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public void close() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public static void main(String[] args) {
try (CloseableClass closeable = new CloseableClass()) {
closeable.internetStuff();
}
catch (IOException e) {
System.out.println("Bad error!");
}
}
}
Let's say you want to deal with the errors thrown in the constructor and the body separately. Is there a supported way to do that? In Python I would do:
try:
closeable = CloseableClass()
except IOException:
print("Constructor error")
return
try:
with closeable:
closeable.internet_stuff()
except IOException:
print("Body error")
but in Java you cannot without assigning a second name to the object:
CloseableClass closeable_;
try {
closeable_ = new CloseableClass();
}
catch (IOException e) {
System.out.println("Constructor error!");
return;
}
try (CloseableClass closeable = closeable_) {
closeable.internetStuff();
}
catch (IOException e) {
System.out.println("Body error!");
}
I have been told that this is "unmaintainable code" primarily due to the use of closeable_
, and I'm not far from agreeing. I wish to avoid using try-finally because then you have the even worse problem of emulating it:
CloseableClass closeable;
try {
closeable = new CloseableClass();
}
catch (IOException e) {
System.out.println("Constructor error!");
return;
}
try {
closeable.internetStuff();
}
catch (IOException e) {
try {
closeable.close();
}
catch (IOException ignore) {
// Already dealing with this
}
System.out.println("Body error!");
}
finally {
try {
closeable.close();
}
catch (IOException e) {
System.out.println("Body error!");
}
}
Note that this requires a second call to close
to be a no-op, which the test class doesn't abide by (note that AutoCloseable
doesn't require this, although Closeable
does). This is a tad nicer when close
can't throw, but not much.
Basically the problem is that
close
can throw- Close before dealing with
IOException
to prevent printing"Body error!"
twice - It's not obvious how to make it work with multiple initializers from the try-with-resources
- You end up duplicating code anyway.
Am I just forced to live with the "unmaintainable code" or am I overlooking a good method to deal with this?
Since Java 9, try-with-resources has accepted ‘effectively final’ variables, so you do not need to reassign the variable.