implementos con cierre o implementos AutoCloseable

128

Estoy en el proceso de aprender Java y no puedo encontrar ninguna buena explicación sobre implements Closeablelas implements AutoCloseableinterfaces.

Cuando implementé un interface Closeable, mi IDE de Eclipse creó un método public void close() throws IOException.

Puedo cerrar la transmisión usando pw.close();sin la interfaz. Pero, no puedo entender cómo puedo implementar el close()método usando la interfaz. ¿Y para qué sirve esta interfaz?

También me gustaría saber: ¿cómo puedo verificar si IOstreamrealmente estaba cerrado?

Estaba usando el código básico a continuación

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
fuente
2
Creo que ya se ha dicho todo, pero tal vez le interese el siguiente artículo sobre probar recursos: docs.oracle.com/javase/tutorial/essential/exceptions/… . Esto también podría ser útil para comprender las respuestas dadas.
Crusam

Respuestas:

40

Me parece que no estás muy familiarizado con las interfaces. En el código que ha publicado, no necesita implementarlo AutoCloseable.

Solo tiene que (o debería) implementar Closeableo AutoCloseablesi está a punto de implementar uno propio PrintWriter, que maneja archivos o cualquier otro recurso que deba cerrarse.

En su implementación, es suficiente llamar pw.close(). Deberías hacer esto en un bloque finalmente:

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) {
      }
   }
}

El código anterior está relacionado con Java 6. En Java 7 esto se puede hacer de manera más elegante (ver esta respuesta ).

Kai
fuente
3
¿Por qué solo con un PrintWriter? Especialmente los AutoClosableobjetos se pueden usar en muchas más circunstancias que solo PrintWriters ...
glglgl
3
Tienes toda la razón. La pregunta era sobre, PrintWriterasí que lo mencioné para ser más específico.
Kai
77
¿Por qué describir la situación de Java 6 en el contexto de AutoCloseable? Mejor mostrar un try-with-resourcesen su lugar ...
ᴠɪɴᴄᴇɴᴛ
191

AutoCloseable(introducido en Java 7) hace posible utilizar el idioma de prueba con recursos :

public class MyResource implements AutoCloseable {

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

}

Ahora puedes decir:

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

y JVM llamará close()automáticamente por usted.

Closeable Es una interfaz más antigua. Por alguna razónPara preservar la compatibilidad con versiones anteriores, los diseñadores de idiomas decidieron crear uno diferente. Esto permite que no solo se usen todas las Closeableclases (como el lanzamiento de secuencias IOException) en try-with-resources, sino que también permite lanzar excepciones comprobadas más generales close().

En caso de duda, use AutoCloseable, los usuarios de su clase se lo agradecerán.

Tomasz Nurkiewicz
fuente
107
La razón es simple: Closeable.close()tiros IOException. Una gran cantidad de close()métodos que podrían beneficiarse de try-con-recursos arrojan otras excepciones controladas (por ejemplo, java.sql.Connection.close()por lo AutoCloseable.close()lanza ExceptionCambio de la existente. CloseableContrato rompería todas las aplicaciones existentes / biblioteca de confiar en el contrato que close()no hace más IOExceptiony no todas las excepciones (marcados).
Marcos Rotteveel
44
@ MarkRotteveel: +1, gracias. Corregí mi respuesta para reflejar sus sugerencias y comentarios.
Tomasz Nurkiewicz
9
Y también: Closeable.close()se requiere ser idempotente. AutoCloseable.close()no lo es, aunque todavía es muy recomendable.
Lukas Eder
2
Además, no use el valor predeterminado public void close( ) throws Exception: use una excepción más específica si puede (por ejemplo, IOException)
gerardw
3
CloseableNo garantiza la idempotencia. Se requiere idempotencia en la implementación de un usuario del close()método. Y si IOExceptiones más específico / apropiado depende del caso de uso.
xdhmoore
71

Closeablese extiende AutoCloseabley está específicamente dedicado a las secuencias de E / S: arroja IOException en lugar de Exception, y es idempotente, mientras que AutoCloseable no proporciona esta garantía.

Todo esto se explica en el javadoc de ambas interfaces.

La implementación de AutoCloseable (o Closeable) permite que una clase se use como un recurso de la construcción de prueba con recursos introducida en Java 7, lo que permite cerrar dichos recursos automáticamente al final de un bloque, sin tener que agregar un bloque finalmente que cierre el recurso explícitamente

Su clase no representa un recurso que se pueda cerrar, y no tiene ningún sentido implementar esta interfaz: un IOTest no se puede cerrar. Ni siquiera debería ser posible instanciarlo, ya que no tiene ningún método de instancia. Recuerde que implementar una interfaz significa que hay una relación is-a entre la clase y la interfaz. No tienes tal relación aquí.

JB Nizet
fuente
55
Simplemente implemente Closable para clases relacionadas con streams y AutoClosable para otros que requiera la función de cierre automático.
lospejos
7

Aquí está el pequeño ejemplo.

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 ");
    }
}

Aquí está la salida:

GeneralTest 
From Close -  AutoCloseable  
From Final Block
Lova Chittumuri
fuente
Es mejor escribir la salida también para que no haya necesidad de un proyecto de prueba para un código tan corto.
raxetul
En el método close (), ¿no necesitamos cerrar el recurso explícitamente? Solo hay una declaración impresa, tal vez.
Shailesh Waghmare
@ShaileshWaghmare sí exactamente. pero para el propósito de la prueba, he mencionado en el recorte de código.
Lova Chittumuri
@LovaChittumuri Entonces, ¿será como this.close()o algo en el código ?, porque se llama automáticamente (solo para estar seguro)
Shailesh Waghmare
@shailesh Waghmare ¿Quieres probarme?
Lova Chittumuri
6

La try-with-resourcesdeclaración

El try-with-resources statementes una trydeclaración que declara uno o más recursos. A resourcees un objeto que debe cerrarse después de que el programa haya terminado con él. Esto try-with-resources statementasegura que cada recurso se cierre al final de la declaración. Cualquier objeto que implemente java.lang.AutoCloseable, que incluye todos los objetos que implementan java.io.Closeable, puede usarse como un recurso.

El siguiente ejemplo lee la primera línea de un archivo. Utiliza una instancia de BufferedReaderpara leer datos del archivo. BufferedReaderes un recurso que debe cerrarse una vez que el programa haya terminado con él:

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

En este ejemplo, el recurso declarado en la instrucción try-with-resources es un BufferedReader. La declaración de declaración aparece entre paréntesis inmediatamente después de la palabra clave try. La clase BufferedReader, en Java SE 7 y posterior, implementa la interfaz java.lang.AutoCloseable. Debido a que la BufferedReaderinstancia se declara en una declaración try-with-resource, se cerrará independientemente de si la declaración try se completa normalmente o abruptamente (como resultado del método que BufferedReader.readLinearroja un IOException).

Antes de Java SE 7, puede usar un finallybloque para asegurarse de que un recurso esté cerrado independientemente de si la instrucción try se completa de manera normal o abrupta. El siguiente ejemplo usa un finallybloque en lugar de una try-with-resourcesdeclaración:

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

}

Por favor, consulte los documentos .

inder
fuente
6

Recientemente leí un libro de Java SE 8 Programmer Guide ii.

He encontrado algo acerca de la diferencia entre AutoCloseablevs Closeable.

La AutoCloseableinterfaz se introdujo en Java 7. Antes de eso, existía otra interfaz llamada Closeable. Era similar a lo que querían los diseñadores de idiomas, con las siguientes excepciones:

  • Closeablerestringe el tipo de excepción lanzada a IOException.
  • Closeable requiere implementaciones para ser idempotente.

Los diseñadores del lenguaje enfatizan la compatibilidad con versiones anteriores. Como cambiar la interfaz existente no era deseable, crearon una nueva llamada AutoCloseable. Esta nueva interfaz es menos estricta que Closeable. Como Closeablecumple con los requisitos para AutoCloseable, comenzó a implementarse AutoCloseablecuando se introdujo este último.

Arvind Katte
fuente
1
En lugar de decir que "Esta nueva interfaz es menos estricta que Closeable", sugiero decir "Esta nueva interfaz se puede usar en contextos más generales, donde la excepción lanzada durante el cierre no es necesariamente una IOException". En el universo Java, ser "menos estricto" tiene un ambiente negativo al respecto.
fuente