Java OCAJP7: statement try-with-resources ed interfacce Closeable ed AutoCloseable

In Java 7 è stato introdotto lo statement try-with-resources che permette di semplificare il rilascio delle risorse da parte dello sviluppatore. Tale costrutto prevede di effettuare l’acquisizione delle risorse direttamente all’interno del try e si preoccupa di chiuderle una volta terminato il loro utilizzo, senza la necessità di farlo esplicitamente all’interno della clausola finally. Le risorse che possono essere gestite tramite lo statement try-with-resources sono solo quelle che implementano l’interfaccia AutoCloseable oppure l’interfaccia Closeable. Occorre inoltre notare che, contrariamente a quanto sembrerebbe intuitivo, l’interfaccia Closeable estende l’interfaccia AutoCloseable e non viceversa. Closeable è infatti definita così:

public interface Closeable extends AutoCloseable

Vediamo ora un esempio di utilizzo del costrutto try-with-resources. Realizziamo un semplice programma che legge un file di testo una riga alla volta e stampa a video il contenuto. Con l’approccio tradizionale, senza utilizzo del try-with-resources dobbiamo cercare di aprire il file all’interno del blocco try, effettuare le operazioni, catchare eventuali eccezioni ed infine, all’interno del blocco finally, chiudere il file dopo aver effettuato un controllo che non sia null.

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ReadingFileExample {

    public static void main(String[] args) {

        String srcFile = "C:\\Users\\MolinariD\\Documents\\testfile.txt";
        BufferedReader inputFile = null;
        try
        {
            inputFile = new BufferedReader(new FileReader(srcFile));
            String line = null;
            while( (line = inputFile.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException fnfe) {
            System.err.println("Impossibile aprire il file " + fnfe.getMessage());
        }
        catch(IOException ioe) {
            System.err.printf("Errore nel processamento del file");
        }
        finally {
            if (inputFile != null) {
                try {
                    inputFile.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Utilizzando il nuovo costrutto introdotto da java 7 invece, possiamo effettuare l’apertura del file contestualmente all’apertura del blocco try inserendola tra parentesi tonde. In questo modo, il try-with-resources si occuperà del rilascio delle risorse al posto nostro e quindi possiamo omettere la clausola finally che in predecenza conteneva il codice per la chiusura.

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ReadingFileExample {

    public static void main(String[] args) {

        String srcFile = "C:\\Users\\MolinariD\\Documents\\testfile.txt";

	// le risorse sono acquisite direttamente dentro al try
        try (BufferedReader inputFile = new BufferedReader(new FileReader(srcFile)))
        {
            String line = null;
            while( (line = inputFile.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException fnfe) {
            System.err.println("Impossibile aprire il file " + fnfe.getMessage());
        }
        catch(IOException ioe) {
            System.err.printf("Errore nel processamento del file");
        }
        // le risorse sono automaticamente chiuse dal try-with-resources
    }
}

Il risultato è un codice più conciso e soprattutto più leggibile e tale vantaggio è ancora più significativo nel caso in cui le risorse da gestire siano molte, perchè il codice per rilasciarle tutte diventerebbe piuttosto corposo.

Modifichiamo l’esempio precedente in modo che il contenuto letto dal file di input non venga più stampato a video ma scritto invece su un altro file di output. In questo caso dobbiamo quindi acquisire e poi chiudere due diverse risorse. Vediamo il codice necessario per implementare questo programma con il metodo tradizionale, precedente a java 7:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class ReadingFileExample {

    public static void main(String[] args) {

        String srcFile = "C:\\Users\\MolinariD\\Documents\\testfile.txt";
        String dstFile = "C:\\Users\\MolinariD\\Documents\\testfile_output.txt";
        BufferedReader inputFile = null;
        BufferedWriter outputFile = null;
        try
        {
            inputFile = new BufferedReader(new FileReader(srcFile));
            outputFile = new BufferedWriter(new FileWriter(dstFile));
            String line = null;
            while( (line = inputFile.readLine()) != null) {
                outputFile.write(line+"\n");
            }
        } catch (FileNotFoundException fnfe) {
            System.err.println("Impossibile aprire il file " + fnfe.getMessage());
        }
        catch(IOException ioe) {
            System.err.printf("Errore nel processamento del file");
        }
        finally {
            if (inputFile != null) {
                try {
                    inputFile.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputFile != null) {
                try {
                    outputFile.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Come vediamo con due risorse da chiudere il codice della clausola finally diventa piuttosto “ingombrante”.
Modifichiamo nuovamente l’esempio in modo da gestire le due risorse con lo statement try-with-resources. Questo costrutto permette infatti di gestire più risorse e per farlo basta semplicemente inserire un ‘;’ (punto e virgola) al termine di ciascuna istruzione di acquisizione di una risorsa, proprio come se fossero delle normali istruzioni.
L’istruzione try per la gestione di entrambe le risorse diventa quindi:

try (BufferedReader inputFile = new BufferedReader(new FileReader(srcFile));
		BufferedWriter outputFile = new BufferedWriter(new FileWriter(dstFile)))
{ ... }

Di seguito è mostrato il codice completo, che come si può vedere risulta significativamente più breve e di più facile comprensione.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class ReadingFileExample {

    public static void main(String[] args) {

        String srcFile = "C:\\Users\\MolinariD\\Documents\\testfile.txt";
        String dstFile = "C:\\Users\\MolinariD\\Documents\\testfile_output.txt";

        try (BufferedReader inputFile = new BufferedReader(new FileReader(srcFile));
                BufferedWriter outputFile = new BufferedWriter(new FileWriter(dstFile)))
        {
            String line = null;
            while( (line = inputFile.readLine()) != null) {
                outputFile.write(line+"\n");
            }
        } catch (FileNotFoundException fnfe) {
            System.err.println("Impossibile aprire il file " + fnfe.getMessage());
        }
        catch(IOException ioe) {
            System.err.printf("Errore nel processamento del file");
        }
    }
}

One thought on “Java OCAJP7: statement try-with-resources ed interfacce Closeable ed AutoCloseable

  1. Pingback: OCAJP snippet #7: ordine di chiusura delle risorse acquisite con un try-with-resources | Dede Blog

Leave a Reply

Your email address will not be published. Required fields are marked *