implémente Closeable ou implémente AutoCloseable

128

Je suis en train d'apprendre Java et je ne trouve aucune bonne explication sur implements Closeableles implements AutoCloseableinterfaces et.

Lorsque j'ai implémenté un interface Closeable, mon IDE Eclipse a créé une méthode public void close() throws IOException.

Je peux fermer le flux en utilisant pw.close();sans l'interface. Mais je ne comprends pas comment je peux implémenter la close()méthode en utilisant l'interface. Et quel est le but de cette interface?

J'aimerais aussi savoir: comment puis-je vérifier si IOstreamc'était vraiment fermé?

J'utilisais le code de base ci-dessous

import java.io.*;

public class IOtest implements AutoCloseable {

public static void main(String[] args) throws IOException  {

    File file = new File("C:\\test.txt");
    PrintWriter pw = new PrintWriter(file);

    System.out.println("file has been created");

    pw.println("file has been created");

}

@Override
public void close() throws IOException {


}
malas
la source
2
Je pense que tout a déjà été dit, mais peut-être que l'article suivant sur les ressources d'essai vous intéresse: docs.oracle.com/javase/tutorial/essential/exceptions/… . Cela peut également être utile pour comprendre les réponses données.
crusam

Réponses:

40

Il me semble que vous n'êtes pas très familier avec les interfaces. Dans le code que vous avez publié, vous n'avez pas besoin de l'implémenter AutoCloseable.

Vous devez seulement (ou devriez) implémenter Closeableou AutoCloseablesi vous êtes sur le point d'implémenter le vôtre PrintWriter, qui gère les fichiers ou toute autre ressource qui doit être fermée.

Dans votre mise en œuvre, il suffit d'appeler pw.close(). Vous devriez le faire dans un bloc finally:

PrintWriter pw = null;
try {
   File file = new File("C:\\test.txt");
   pw = new PrintWriter(file);
} catch (IOException e) {
   System.out.println("bad things happen");
} finally {
   if (pw != null) {
      try {
         pw.close();
      } catch (IOException e) {
      }
   }
}

Le code ci-dessus est lié à Java 6. Dans Java 7, cela peut être fait plus élégamment (voir cette réponse ).

Kai
la source
3
Pourquoi seulement avec un PrintWriter? Surtout les AutoClosableobjets peuvent être utilisés dans beaucoup plus de circonstances que juste PrintWriters ...
glglgl
3
Tu as tout à fait raison. La question concernait PrintWriterdonc je l'ai mentionné pour être plus précis.
Kai
7
Pourquoi décrire la situation de Java 6 dans le contexte de AutoCloseable? Mieux vaut montrer un à la try-with-resourcesplace…
ᴠɪɴᴄᴇɴᴛ
191

AutoCloseable(introduit dans Java 7) permet d'utiliser l' idiome try-with-resources :

public class MyResource implements AutoCloseable {

    public void close() throws Exception {
        System.out.println("Closing!");
    }

}

Maintenant, vous pouvez dire:

try (MyResource res = new MyResource()) {
    // use resource here
}

et JVM appellera close()automatiquement pour vous.

Closeable est une interface plus ancienne. Pour certaines raisonsPour préserver la compatibilité descendante, les concepteurs de langage ont décidé d'en créer un distinct. Cela permet non seulement d'utiliser toutes les Closeableclasses (comme le lancement de flux IOException) dans try-with-resources, mais permet également de lancer des exceptions vérifiées plus générales à partir de close().

En cas de doute, utilisez AutoCloseable , les utilisateurs de votre classe vous en seront reconnaissants.

Tomasz Nurkiewicz
la source
107
La raison est simple: Closeable.close()jette IOException. Un grand nombre de close()méthodes qui pourraient bénéficier de try-with-resources lancent d'autres exceptions vérifiées (par exemple java.sql.Connection.close(), AutoCloseable.close()jette ainsi Exception. La modification du Closeablecontrat existant casserait toutes les applications / bibliothèques existantes en se basant sur le contrat qui close()ne lève que IOExceptionet pas toutes les exceptions (vérifiées).
Marquer Rotteveel
4
@MarkRotteveel: +1, merci. J'ai corrigé ma réponse pour refléter vos suggestions et commentaires.
Tomasz Nurkiewicz
9
Et aussi: Closeable.close()doit être idempotent. AutoCloseable.close()n'est pas, bien que cela soit toujours fortement recommandé.
Lukas Eder
2
De plus, n'utilisez pas la valeur par défaut public void close( ) throws Exception- utilisez une exception plus spécifique si vous le pouvez (par exemple, IOException)
gerardw
3
Closeablene garantit pas l' idempotence. Cela nécessite une idempotence dans l'implémentation de la close()méthode par un utilisateur . Et le fait que ce IOExceptionsoit plus spécifique / approprié dépend du cas d'utilisation.
xdhmoore
71

Closeables'étend AutoCloseable, et est spécifiquement dédié aux flux IO: il lance IOException au lieu d'Exception, et est idempotent, alors qu'AutoCloseable ne fournit pas cette garantie.

Tout cela est expliqué dans le javadoc des deux interfaces.

L'implémentation d'AutoCloseable (ou Closeable) permet à une classe d'être utilisée comme ressource de la construction try-with-resources introduite dans Java 7, qui permet de fermer automatiquement ces ressources à la fin d'un bloc, sans avoir à ajouter un bloc finally qui se ferme la ressource explicitement.

Votre classe ne représente pas une ressource fermable, et il n'y a absolument aucun intérêt à implémenter cette interface: un IOTest ne peut pas être fermé. Il ne devrait même pas être possible de l'instancier, car il n'a aucune méthode d'instance. N'oubliez pas que l'implémentation d'une interface signifie qu'il existe une relation is-a entre la classe et l'interface. Vous n'avez pas une telle relation ici.

JB Nizet
la source
5
Implémentez simplement Closable pour les classes liées aux flux et AutoClosable pour les autres, ce qui nécessite une fonction de fermeture automatique.
lospejos
7

Voici le petit exemple

public class TryWithResource {

    public static void main(String[] args) {
        try (TestMe r = new TestMe()) {
            r.generalTest();
        } catch(Exception e) {
            System.out.println("From Exception Block");
        } finally {
            System.out.println("From Final Block");
        }
    }
}



public class TestMe implements AutoCloseable {

    @Override
    public void close() throws Exception {
        System.out.println(" From Close -  AutoCloseable  ");
    }

    public void generalTest() {
        System.out.println(" GeneralTest ");
    }
}

Voici la sortie:

GeneralTest 
From Close -  AutoCloseable  
From Final Block
Lova Chittumuri
la source
Il est préférable d'écrire également la sortie afin qu'il n'y ait pas besoin d'un projet d'essai pour un tel code court.
raxetul
Dans la méthode close (), n'avons-nous pas besoin de fermer la ressource explicitement? Il n'y a peut-être qu'une déclaration imprimée.
Shailesh Waghmare le
@ShaileshWaghmare oui exactement. mais à des fins de test, j'ai mentionné dans l'extrait de code.
Lova Chittumuri le
@LovaChittumuri Alors est-ce que ce sera comme this.close()ou quelque chose dans le code?, Parce qu'il est appelé automatiquement. (Juste pour être sûr)
Shailesh Waghmare
@shailesh Waghmare Voulez-vous me tester.
Lova Chittumuri le
6

La try-with-resourcesdéclaration.

Le try-with-resources statementest une tryinstruction qui déclare une ou plusieurs ressources. A resourceest un objet qui doit être fermé une fois le programme terminé. Le try-with-resources statementgarantit que chaque ressource est fermée à la fin de l'instruction. Tout objet implémentant java.lang.AutoCloseable, qui comprend tous les objets implémentés java.io.Closeable, peut être utilisé comme ressource.

L'exemple suivant lit la première ligne d'un fichier. Il utilise une instance de BufferedReaderpour lire les données du fichier. BufferedReaderest une ressource qui doit être fermée une fois le programme terminé:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

Dans cet exemple, la ressource déclarée dans l'instruction try-with-resources est un BufferedReader. L'instruction de déclaration apparaît entre parenthèses immédiatement après le mot-clé try. La classe BufferedReader, dans Java SE 7 et versions ultérieures, implémente l'interface java.lang.AutoCloseable. Étant donné que l' BufferedReaderinstance est déclarée dans une instruction try-with-resource, elle sera fermée indépendamment du fait que l'instruction try se termine normalement ou brusquement (à la suite de la BufferedReader.readLinegénération d'un IOException).

Avant Java SE 7, vous pouvez utiliser un finallybloc pour vous assurer qu'une ressource est fermée, que l'instruction try se termine normalement ou brusquement. L'exemple suivant utilise un finallybloc au lieu d'une try-with-resourcesinstruction:

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }

}

Veuillez vous référer à la documentation .

inder
la source
6

Récemment, j'ai lu un livre Java SE 8 Programmer Guide ii.

J'ai trouvé quelque chose au sujet de la différence entre AutoCloseablevs Closeable.

L' AutoCloseableinterface a été introduite dans Java 7. Avant cela, il existait une autre interface appelée Closeable. C'était similaire à ce que voulaient les concepteurs de langage, avec les exceptions suivantes:

  • Closeablerestreint le type d'exception à IOException.
  • Closeable exige que les implémentations soient idempotentes.

Les concepteurs de langage mettent l'accent sur la compatibilité descendante. Comme le changement de l'interface existante n'était pas souhaitable, ils en ont créé une nouvelle appelée AutoCloseable. Cette nouvelle interface est moins stricte que Closeable. Puisque Closeablerépond aux exigences de AutoCloseable, il a commencé à être mis en œuvre AutoCloseablelorsque ce dernier a été introduit.

Arvind Katte
la source
1
Au lieu de dire que "Cette nouvelle interface est moins stricte que Closeable", je suggérerais de dire "Cette nouvelle interface peut être utilisée dans des contextes plus généraux, où l'exception lancée lors de la fermeture n'est pas nécessairement une IOException". Dans l'univers Java, être "moins strict" a une ambiance négative à ce sujet.
fountainhead