189 votes

Idiome correct pour gérer plusieurs ressources enchaînés dans le bloc try-avec-ressources ?

La Java 7 essayez-avec-les ressources de la syntaxe (également connu sous le BRAS (blocAutomatique de Gestion de la Ressource)) c'est sympa, court et simple lors de l'utilisation d'un seul AutoCloseable des ressources. Cependant, je ne suis pas sûr de ce qui est la bonne idiome quand j'ai besoin de déclarer plusieurs ressources qui dépendent les uns des autres, par exemple un FileWriter et BufferedWriter qui l'enveloppe. Bien sûr, cette question concerne tout cas, si certains AutoCloseable des ressources sont enveloppés, non seulement ces deux classes spécifiques.

Je suis venu avec les trois options suivantes:

1)

Le naïf idiome j'ai vu, c'est de déclarer que le haut-niveau wrapper dans le BRAS-géré variable:

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 belle et courte, mais il est cassé. Parce que le sous-jacent FileWriter n'est pas déclaré dans une variable, il ne sera jamais fermée directement dans le générés finally bloc. Il sera fermé uniquement par le biais de l' close méthode de l'emballage BufferedWriter. Le problème, c'est que si une exception est levée à partir de la bws'constructeur, close ne sera pas appelé 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, les deux sous-jacent et de l'emballage des ressources sont déclarées dans le BRAS géré variables, de sorte que les deux d'entre eux seront certainement fermé, donc certainement que le sous-jacent fw.close() sera appelée deux fois, directement pour la première et pour la seconde fois par l'habillage bw.close().

Cela ne devrait pas être un problème pour ces deux classes qui implémentent Closeable (qui est un sous-type d' AutoCloseable), dont le contrat stipule que plusieurs appels à l' close sont autorisés:

Ferme ce flux et libère toutes les ressources du système associé. Si le flux est déjà fermé, puis d'invoquer cette méthode n'a aucun effet.

Toutefois, dans le cas général, je peux avoir des ressources qui ne implémenter AutoCloseable (et pas Closeable), ce qui ne garantit pas que l' close peut être appelée plusieurs fois:

Notez que contrairement à la méthode close de java.io.Fermer, cette méthode close n'est pas nécessaire d'être idempotent. En d'autres termes, l'appel de cette méthode close plus d'une fois peut avoir certains visible effet secondaire, contrairement à la Fermer.proche qui est nécessaire pour n'avoir aucun effet si elle est appelée plus d'une fois. Cependant, les responsables de l'implémentation de cette interface sont fortement encouragés à faire leurs proches méthodes idempotent.

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 seule l' fw représente une véritable ressource qui a besoin d'être nettoyé. L' bw n'est pas elle-même une ressource, il ne délègue à l' fw, de sorte qu'il devrait être suffisant de fermer le sous-jacent fw.

D'autre part, la syntaxe est un peu irrégulière et aussi, Eclipse émet un avertissement, je crois que c'est une fausse alerte, mais il est encore un avertissement que l'on a à traiter:

Fuite de ressource: 'bw' est jamais fermé


Donc, quelle approche? Ou ai-je raté quelque autre idiome qui est le bon ?

82voto

Voici mon point de vue sur les solutions de rechange:

1)

try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
    bw.write(text);
}

Pour moi, la meilleure chose à venir à Java à partir de traditionnelle C++ il y a 15 ans était qu'on pouvait faire confiance à votre programme. Même si les choses sont dans la boue et le mal, qui, souvent, ils, je veux le reste du code pour être sur le meilleur comportement et à l'odeur des roses. En effet, l' BufferedWriter peut lever une exception ici. À cours de mémoire ne serait pas surprenant, par exemple. Pour d'autres décorateurs, savez-vous qui de la java.io classes wrapper jeter un checked exception de leurs constructeurs? Je n'ai pas. Ne pas faire de code d'intelligibilité beaucoup de bien si vous vous fiez à ce genre d'obscurs de la connaissance.

Il y a également la "destruction". Si une condition d'erreur, alors vous ne voulez probablement pas à être rinçage des bêtises à un fichier qui a besoin d'une suppression (code non montré). Bien que, bien sûr, la suppression du fichier est également une autre opération intéressante à faire d'erreur de manipulation.

En règle générale, vous souhaitez finally blocs à être plus courte et la plus fiable possible. L'ajout de bouffées de chaleur n'aide pas à cet objectif. Pour de nombreux communiqués de certains de la mise en mémoire tampon les classes du JDK avait un bug où une exception à partir d' flush dans close causée close sur l'objet décoré de ne pas être appelé. Alors que cela a été corrigé depuis un certain temps, s'attendre à d'autres implémentations.

2)

try (
    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw)
) {
    bw.write(text);
}

Nous sommes toujours rinçage dans l'implicite bloc finally (maintenant avec la répétition close - cela devient pire à mesure que vous ajoutez plus de décorateurs), mais la construction est sûr et nous devons implicite enfin les blocs de sorte que même un échec de l' flush n'empêche pas les ressources de la libération.

3)

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
}

Il y a un bug ici. Devrait être:

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
    bw.flush();
}

Certains mal mis en œuvre, les décorateurs sont en fait de ressources et doivent être fermés de manière fiable. Aussi certains cours d'eau peuvent avoir besoin d'être fermé d'une façon particulière (peut-être qu'ils sont en train de faire de la compression et de la nécessité d'écrire des bits pour finir, et ne pouvez pas simplement vider.

Verdict

Bien que le 3 est techniquement supérieur à la solution, le développement de logiciels pour des raisons de 2 le meilleur choix. Cependant, essayez-avec-des ressources est encore insuffisante fix et vous devriez coller avec l'Exécution Autour de l'idiome, qui doit avoir une syntaxe plus claire avec des fermetures en Java SE 8.

24voto

Daniel C. Sobral Points 159554

Le premier style est celui proposé par Oracle. BufferedWriter ne jette pas vérifié exceptions, de sorte que si une exception est levée, le programme ne devrait pas s'en remettre, ressources récupérer tout à fait discutable.

Principalement parce que cela pourrait arriver dans un thread, le thread en train de mourir, mais le programme continue -- dire, il y a une mémoire temporaire interruption de service qui n'était pas assez long pour nuire gravement à la suite du programme. C'est un peu le coin des cas, cependant, et si il arrive assez souvent pour rendre la fuite des ressources d'un problème, le try-with-resources est le moindre de vos problèmes.

5voto

Jeanne Boyarsky Points 5968

Option 4

Changer vos ressources à Fermer, pas AutoClosable si vous le pouvez. Le fait que les constructeurs peuvent être enchaînés implique qu'il n'est pas rare de fermer la ressource à deux reprises. (Ce qui était vrai avant BRAS). En savoir plus sur ce ci-dessous.

Option 5

N'utilisez pas les BRAS et code très soigneusement pour s'assurer close() n'est pas appelé deux fois!

Option 6

N'utilisez pas les BRAS et ont votre enfin close() appelle dans un try/catch eux-mêmes.

Pourquoi je ne pense pas que ce problème est propre à BRAS

Dans tous ces exemples, la enfin close() appels doivent être dans un bloc catch. Laissé de côté pour des raisons de lisibilité.

Pas bon parce que fw peut être fermée à deux reprises. (ce qui est bien pour FileWriter, mais pas dans votre hypothetial exemple):

FileWriter fw = null;
BufferedWriter bw = null'
try {
  fw = new FileWriter(file)) {
  bw = new BufferedWriter(fw);
  bw.write(text);
} finally {
  if ( fw != null ) fw.close();
  if ( bw != null ) bw.close();
}

Pas bon parce que fw pas fermé si d'exception sur la construction d'un BufferedWriter. (encore une fois, ne peut pas se produire, mais dans votre exemple hypothétique):

FileWriter fw = null;
BufferedWriter bw = null'
try {
  fw = new FileWriter(file)) {
  bw = new BufferedWriter(fw);
  bw.write(text);
} finally {
  if ( bw != null ) bw.close();
}

2voto

Anon Points 68

Je voulais juste appuyer sur la suggestion de Jeanne Boyarsky de ne pas utiliser le bras, mais en s’assurant que le FileWriter est toujours fermée exactement une fois. Ne pense pas qu’il y a des problèmes ici...

Je suppose que le bras étant juste syntaxique, nous ne pouvons pas toujours l’utiliser pour remplacer enfin blocs. Tout comme nous ne pouvons pas toujours utiliser une boucle foreach faire quelque chose qui est possible avec les itérateurs.

2voto

poison Points 69

Étant donné que vos ressources sont imbriqués, vos clauses try-avec devraient être aussi :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X