La syntaxe Java 7 try-with-resources (également connue sous le nom de bloc ARM ( Automatic Resource Management )) est agréable, courte et simple lors de l'utilisation d'une seule AutoCloseable
ressource. Cependant, je ne sais pas quelle est l'idiome correct lorsque je dois déclarer plusieurs ressources qui dépendent les unes des autres, par exemple a FileWriter
et aBufferedWriter
qui l'enveloppent. Bien entendu, cette question concerne tous les cas où certaines AutoCloseable
ressources sont encapsulées, pas seulement ces deux classes spécifiques.
J'ai proposé les trois alternatives suivantes:
1)
L'idiome naïf que j'ai vu est de déclarer uniquement le wrapper de niveau supérieur dans la variable gérée par ARM:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
C'est joli et court, mais c'est cassé. Comme le sous FileWriter
- jacent n'est pas déclaré dans une variable, il ne sera jamais fermé directement dans le finally
bloc généré . Il sera fermé uniquement par la close
méthode de l'emballage BufferedWriter
. Le problème est que si une exception est levée depuis le bw
constructeur de s, close
elle ne sera pas appelée et donc le sous-jacent FileWriter
ne sera pas fermé .
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Ici, la ressource sous-jacente et la ressource d'encapsulation sont déclarées dans les variables gérées par ARM, donc les deux seront certainement fermées, mais le sous-jacent fw.close()
sera appelé deux fois : non seulement directement, mais également via l'encapsulation bw.close()
.
Cela ne devrait pas être un problème pour ces deux classes spécifiques qui implémentent toutes les deux Closeable
(qui est un sous-type de AutoCloseable
), dont le contrat stipule que plusieurs appels àclose
sont autorisés:
Ferme ce flux et libère toutes les ressources système qui lui sont associées. Si le flux est déjà fermé, l'invocation de cette méthode n'a aucun effet.
Cependant, dans un cas général, je peux avoir des ressources qui implémentent uniquement AutoCloseable
(et non Closeable
), ce qui ne garantit pas queclose
puissent être appelées plusieurs fois:
Notez que contrairement à la méthode close de java.io.Closeable, cette méthode close n'a pas besoin d'être idempotente. En d'autres termes, appeler cette méthode close plus d'une fois peut avoir un effet secondaire visible, contrairement à Closeable.close qui doit n'avoir aucun effet s'il est appelé plus d'une fois. Cependant, les implémenteurs de cette interface sont fortement encouragés à rendre leurs méthodes proches idempotentes.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Cette version devrait être théoriquement correcte, car seul le fw
représente une ressource réelle qui doit être nettoyée. Le bw
lui-même ne contient aucune ressource, il ne délègue qu'au fw
, il devrait donc suffire de fermer uniquement le sous-jacent fw
.
D'un autre côté, la syntaxe est un peu irrégulière et aussi, Eclipse émet un avertissement, qui je crois est une fausse alerte, mais c'est quand même un avertissement auquel il faut faire face:
Fuite de ressources: 'bw' n'est jamais fermé
Alors, quelle approche adopter? Ou ai-je manqué un autre idiome qui est le bon ?
public BufferedWriter(Writer out, int sz)
peut lancer un fichier IllegalArgumentException
. De plus, je peux étendre BufferedWriter avec une classe qui lèverait quelque chose de son constructeur ou créerait un wrapper personnalisé dont j'ai besoin.
BufferedWriter
constructeur peut facilement lever une exception. OutOfMemoryError
est probablement le plus courant car il alloue une bonne partie de la mémoire pour le tampon (bien qu'il puisse indiquer que vous souhaitez redémarrer l'ensemble du processus). / Vous avez besoin de flush
votre BufferedWriter
si vous ne fermez pas et que vous souhaitez conserver le contenu (généralement uniquement le cas sans exception). FileWriter
ramasse tout ce qui se trouve être le codage de fichier "par défaut" - il vaut mieux être explicite.