W3docs

Java try-with-resources

Chiudi automaticamente le risorse AutoCloseable in Java con l'istruzione try-with-resources.

L'istruzione try-with-resources è la soluzione Java al pattern "apri, usa, chiudi sempre". Si dichiara una risorsa all'inizio del try, e la JVM garantisce che verrà chiusa all'uscita del blocco — che il blocco sia terminato normalmente, abbia lanciato un'eccezione, o sia stato abbandonato per qualsiasi altro motivo di controllo del flusso. L'ingombrante danza con finally si riduce a una singola dichiarazione.

Questo capitolo tratta la sintassi, cosa si qualifica come risorsa, come vengono chiuse più risorse, il meccanismo delle eccezioni soppresse che rende l'istruzione affidabile, e quando non usarla.

La forma

try (FileReader reader = new FileReader(path)) {
  // use reader
}

Tutto qui. Niente finally, niente close() esplicito. Quando il try termina, la JVM chiama reader.close() automaticamente. Qualsiasi clausola catch e finally aggiunta funziona comunque — viene eseguita dopo la chiusura della risorsa.

try (FileReader reader = new FileReader(path)) {
  // use reader
} catch (IOException e) {
  // handle — at this point the reader is already closed
}

Cosa può essere una risorsa

Una risorsa deve implementare AutoCloseable (o la sua più vecchia sottointerfaccia Closeable). Quell'interfaccia dichiara un unico metodo:

public interface AutoCloseable {
  void close() throws Exception;
}

La maggior parte dei tipi che si usano comunemente la implementa già: InputStream, OutputStream, Reader, Writer, Connection, Statement, ResultSet, Scanner, wrapper per Lock e molti client di terze parti. Se si scrive una classe che possiede una risorsa, si implementa AutoCloseable direttamente.

Più risorse

Le dichiarazioni si separano con punti e virgola. Le risorse vengono chiuse in ordine inverso rispetto alla dichiarazione, quindi l'ultima aperta è la prima a essere chiusa:

try (
  FileInputStream  in  = new FileInputStream(src);
  FileOutputStream out = new FileOutputStream(dst)
) {
  in.transferTo(out);
}
// closes `out` first, then `in`

L'ordine inverso è importante perché la seconda risorsa spesso dipende dalla prima. Chiudere prima quella dipendente lascia la base intatta per un momento in più, il che è l'ordine più sicuro.

Riutilizzo di risorse esistenti (Java 9+)

Se si ha già una variabile che contiene un AutoCloseable, non è necessario dichiararne una nuova — la si passa per nome:

BufferedReader reader = openSomewhere();
try (reader) {
  // use it
}

La variabile deve essere final o effettivamente final (non si può riassegnarla). Questa forma è utile quando una risorsa viene costruita altrove — al costo di rendere meno visibile la chiusura. Va usata quando la variabile esistente è proprio lì; altrimenti è preferibile la dichiarazione inline.

Eccezioni soppresse

Ecco la parte sottile. Cosa succede se il corpo del try lancia un'eccezione e anche la chiamata a close() ne lancia una? Prima di Java 7, l'eccezione di chiusura del finally avrebbe sostituito quella originale — perdendo così la causa reale.

try-with-resources risolve questo problema. L'eccezione originale è quella lanciata al chiamante; qualsiasi eccezione generata durante la chiusura viene allegata come eccezione soppressa, recuperabile tramite Throwable.getSuppressed():

try (Resource r = new Resource()) {
  r.use();          // throws A
}                   // close() throws B
// caller sees A
// A.getSuppressed() returns [B]

Il printStackTrace() predefinito stampa anche le eccezioni soppresse (righe Suppressed: ... sotto quella principale). Quasi non è mai necessario gestirlo manualmente — il linguaggio preserva la catena per te.

Una forma reale tipica

Un pattern comune, mettendo tutto insieme:

public List<String> readAllLines(Path p) throws IOException {
  try (BufferedReader reader = Files.newBufferedReader(p)) {
    return reader.lines().toList();
  }
}

Cose da notare: nessuna chiusura esplicita, nessun finally, il corpo può fare return direttamente, e il chiamante riceve comunque una IOException pulita se qualcosa è andato storto.

Quando try-with-resources è lo strumento sbagliato

È lo strumento giusto quando si possiede l'intero ciclo di vita della risorsa per un blocco. È sbagliato quando:

  • La risorsa vive più a lungo del blocco — ad esempio, una connessione mantenuta in cache tra le chiamate. Chiuderla alla fine del metodo interromperebbe il prossimo utente.
  • Non si possiede la risorsa — un chiamante l'ha passata. La chiuderà lui stesso.

In questi casi, lasciare la chiusura al proprietario. Se non si riesce a capire chi sia il proprietario, è un odore di design — bisogna scoprirlo prima di scrivere il codice.

Un esempio pratico

Un breve programma che copia una stringa in una pipe in memoria e la rilegge attraverso un buffered reader. Entrambi gli stream sono AutoCloseable; entrambi vengono chiusi automaticamente; viene aggiunta una piccola risorsa personalizzata per vedere l'ordine di chiusura nell'output.

java— editable, runs on the server

Nell'output si può vedere che l'ordine di dichiarazione è a, b, src, buf ma l'ordine di chiusura è inverso: buf, src, b, a. Questa è la garanzia del linguaggio — il primo aperto, l'ultimo chiuso.

Cosa c'è dopo

Finora abbiamo solo gestito eccezioni lanciate da Java. Il codice reale deve anche sollevarne di proprie — per argomenti non validi, invarianti violate o errori di dominio. Continua con Java throw e throws.

Pratica

Pratica
Un blocco `try-with-resources` apre due risorse `AutoCloseable`. Il corpo lancia un `IOException` e il metodo `close()` della seconda risorsa lancia anch'esso un `IOException`. Cosa vede il chiamante?
Un blocco `try-with-resources` apre due risorse `AutoCloseable`. Il corpo lancia un `IOException` e il metodo `close()` della seconda risorsa lancia anch'esso un `IOException`. Cosa vede il chiamante?
Was this page helpful?