Sunday 25 November 2012

Java Cafe 2 : try-with-resources statement in Java 7

Prior to Java 7, if you open a resource like a file or network connection in a method, you will typically write code like below, provided that you want to handle all exceptions inside the method. This method prints the first character in a text file, and prints the stack trace of any exceptions thrown:


public void printFirstChar() {
    FileReader reader = null;
    try {
        reader = new FileReader("ex.txt");
        System.out.println((char)reader.read());
    } catch (IOException ioe) {
        ioe.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

It is necessary close the resource in a finally block to guarantee the file is closed even if there were exceptions thrown, and the finally block must catch the IOException thrown by the reader.close() method. Because of closing in finally, and also you want to catch the FileNotFoundException (as an IOException) from FileReader constructor, you need to declare reader outisde the try and initialize it to null. This is obviously very clumsy.

The try-with-resources statement introduced in Java 7 solves all this. A resource is any object that must be closed after the program is done with it. This try-with-resources statement automatically closes the resource (in our case the reader) at the end of the statement. Any object that implements java.lang.AutoCloseable (new in Java 7, and simply has a close() method) can be used as a resource in the try-with-resources statement. The interface java.io.Closeable extends AutoCloseable, so any Closeable object can be used as a resource. This includes all commonly used classes in the java.io package. Using try-with-resources, our example above can be succinctly written as:

public void printFirstChar() {
    try (FileReader reader = new FileReader("ex.txt")) {
        System.out.println((char)reader.read());
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
}

17 lines of code shortened to 7! The reader object will be closed at the end of the try-with-resources statement regardless of any exception thrown. Note that if the file ex.txt does not exist, then the FileNotFoundException will be caught by the catch clause, and the stack trace of FileNotFoundException will be printed. If you do not have the catch clause, then your printFirstChar() method must throw IOException, like:

public void printFirstChar() throws IOException {
    try (FileReader reader = new FileReader("ex.txt")) {
        System.out.println((char)reader.read());
    }
}

What if an exception is thrown when reader.read() is called, and then when try-with-resources closes reader, an exception is thrown as well? The exception from reader.read() will be thrown by the printFirstChar() method, while the exception from the close will be suppressed. These suppressed exceptions can be retrieved by the calling method using the Throwable.getSuppressed() method like below:

try {
    printFirstChar();
} catch (IOException ioe) {
    for (Throwable t : ioe.getSuppressed()) {
        // do something with the suppressed exception
    }
}

No comments:

Post a Comment