Implementing the AutoCloseable Interface
A declared resource must provide the close() method that will be called when the resource is to be closed. This is guaranteed by the fact that the resource must implement the java.lang.AutoCloseable interface which specifies the close() method. The compiler only allows resource declarations or resource variables in the try header that are AutoCloseable. Many classes in the Java API implement the AutoCloseable interface: byte input streams, byte output streams, readers and writers in the java.io package (Chapter 20, p. 1231), and database connections, query statements, and data result sets in the java.sql package (Chapter 24, p. 1511).
The abstract void method close() in the java.lang.AutoCloseable interface is declared to throw an Exception. Files and streams implement the java.io.Closeable interface which extends the AutoCloseable interface, but the abstract method close() in the Closeable interface is specified to throw an IOException, a subtype of Exception.
interface AutoCloseable { // java.lang package
void close() throws Exception;
}
interface Closeable extends AutoCloseable { // java.io package
void close() throws IOException; // Mandatory idempotent
}
What is important is that any class implementing the AutoCloseable interface can override the throws clause of the abstract close() method. The implementation of the close() method can choose to throw any Exception, meaning either an Exception or subtypes of Exception, but also throw no exception at all if the method cannot fail.
An implementation of the close() method of the Closeable interface must be idempotent. There is no such requirement for implementations of the close() method of the AutoCloseable interface, but it is highly recommended that they are idempotent.
In Example 7.18, the class Gizmo implements the AutoCloseable interface. The class uses the field closed to keep track of whether the gizmo is closed or not. The close() method at (2) reports whether the gizmo is already closed; if not, it sets the closed flag to true and throws an unchecked IllegalArgumentException. The close() method is idempotent, as it will only report that the gizmo is already closed once it has been called. The class Gizmo also has the method compute() at (3), that throws an unchecked ArithmeticException.
The class GizmoTest uses the Gizmo class. It declares and initializes a Gizmo in a try-with-resources statement at (4), and calls its compute() method. The output shows that the unchecked ArithmeticException thrown by the compute() method is caught by the catch clause at (5), after the close() method has been called on the resource—any catch clauses and finally clause explicitly specified with an extended try-with-resources statement are executed after the declared resources have been closed.
Example 7.18 Implementing the AutoCloseable Interface
// File: Gizmo.java
public class Gizmo implements AutoCloseable {
private boolean closed = false; // (1) Closed if true
@Override
public void close() { // (2) Idempotent
System.out.println(“Enter: close()”);
if (closed) {
System.out.println(“Already closed”);
} else {
closed = true;
System.out.println(“Gizmo closed”);
System.out.println(“Throwing IllegalArgumentException in close()”);
throw new IllegalArgumentException(“thrown in close()”); // Suppressed
}
System.out.println(“Exit: close()”); // Only executed if already closed.
}
public void compute() { // (3)
System.out.println(“Enter: compute()”);
System.out.println(“Throwing ArithmeticException in compute()”);
throw new ArithmeticException(“thrown in compute()”);
}
}
// File: GizmoTest.java
public class GizmoTest {
public static void main(String[] args) {
try (var myGizmo = new Gizmo()) { // (4)
myGizmo.compute();
} catch (Exception ex) { // (5)
System.out.println(“Printing stack trace in catch clause of main():”);
ex.printStackTrace();
} finally {
System.out.println(“Finally: Done in main()”);
}
}
}
// File: GizmoTest2.java
public class GizmoTest2 {
public static void main(String[] args) {
try (var myGizmo = new Gizmo()) {
myGizmo.compute();
} catch (Exception ex) { // (6)
System.out.println(“Exception caught in the catch clause of main():\n\t”
+ ex);
System.out.println(“Printing suppressed exceptions “
+ “in the catch clause of main():”);
Throwable[] supressedEx = ex.getSuppressed(); // (7)
for (Throwable t : supressedEx) {
System.out.println(“\t” + t);
}
} finally {
System.out.println(“Finally: Done in main()”);
}
}
}
Output from running GizmoTest:
Enter: compute()
Throwing ArithmeticException in compute()
Enter: close()
Gizmo closed
Throwing IllegalArgumentException in close()
Printing stack trace in catch clause of main():
java.lang.ArithmeticException: thrown in compute()
at Gizmo.compute(Gizmo.java:21)
at GizmoTest.main(GizmoTest.java:5)
Suppressed: java.lang.IllegalArgumentException: thrown in close()
at Gizmo.close(Gizmo.java:13)
at GizmoTest.main(GizmoTest.java:6)
Finally: Done in main()
Output from running GizmoTest2:
Enter: compute()
Throwing ArithmeticException in compute()
Enter: close()
Gizmo closed
Throwing IllegalArgumentException in close()
Exception caught in the catch clause of main():
java.lang.ArithmeticException: thrown in compute()
Printing suppressed exceptions in the catch clause of main():
java.lang.IllegalArgumentException: thrown in close()
Finally: Done in main()
Leave a Reply