Skip to content

Instantly share code, notes, and snippets.

@ktul
Created December 13, 2021 14:53
Show Gist options
  • Select an option

  • Save ktul/4efa279329d3eb583bbc517857e2aa4c to your computer and use it in GitHub Desktop.

Select an option

Save ktul/4efa279329d3eb583bbc517857e2aa4c to your computer and use it in GitHub Desktop.
Try-with-resources example and an equivalent old-style implementation
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;
class TryWithResources {
public static void main(String[] args) {
System.out.println("with resources:");
withResource();
System.out.println("\nold-style:");
oldStyle();
System.out.println("\nwrong old style:");
wrongOldStyle();
}
// Some example of a try-with-resources.
private static void withResource() {
try (var a = new Closeable("a"); var b = new Closeable("b")) {
a.fail();
} catch (Exception e) {
handleException(e);
} finally {
System.out.println("finally");
}
}
// This method has the same behaviour as withResource(), but doesn't use resources
// Remarks:
// - The implementation details and bytecode might differ.
// - This was not tested with shared resources, so there might be some synchronization missing.
private static void oldStyle() {
try {
var a = new Closeable("a");
var b = new Closeable("b");
try {
a.fail();
} catch (Exception e) {
try {
b.close();
} catch (Exception e2) {
e.addSuppressed(e2);
}
try {
a.close();
} catch (Exception e2) {
e.addSuppressed(e2);
}
throw e;
}
b.close();
a.close();
} catch (Exception e) {
handleException(e);
} finally {
System.out.println("finally");
}
}
// One could easily but wrongly assume this is what happens during a try-with-resources.
// But here's what's wrong:
// - Exceptions are ignored, resources are not closed and finally might not be executed.
// Worst case:
// - b.close #1 throws
// -> a.close #1 is never reached -> a is not closed
// -> (if the exception handler exits: throw e is never reached -> e is ignored)
// -> b.close #2 is called on a possibly closed b
// -> b.close throws again
// -> a.close #1 is never reached -> a is never closed
// -> (if the exception handler exits: finally is never reached)
private static void wrongOldStyle() {
var a = new Closeable("a");
var b = new Closeable("b");
try {
a.fail();
} catch (Exception e) {
try {
b.close(); // #1
a.close(); // #1
} catch (IOException e2) {
handleException(e2);
}
handleException(e);
} finally {
try {
b.close(); // #2
a.close(); // #2
} catch (IOException e2) {
handleException(e2);
}
System.out.println("finally");
}
}
private static void handleException(Exception e) {
var suppressed = Arrays.stream(e.getSuppressed())
.map(Throwable::getMessage)
.collect(Collectors.joining(", "));
var message = "caught exception from " + e.getMessage() + " with "
+ (suppressed.isEmpty() ? "nothing" : suppressed)
+ " suppressed";
System.out.println(message);
}
private final static class Closeable implements AutoCloseable {
private final String name;
Closeable(String name) /*throws IOException*/ {
System.out.println(name + ".new");
this.name = name;
// throw new IOException(name + ".new"); // alternative when constructor throws
}
public void fail() throws IOException {
System.out.println(name + ".fail");
throw new IOException(name + ".fail");
}
public void close() throws IOException {
System.out.println(name + ".close");
throw new IOException(name + ".close");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment