2011-07-31 16 views
22

¿Es posible ignorar la excepción lanzada cuando se cierra un recurso usando una declaración try-with-resources?Cerrar el recurso silenciosamente usando try-with-resources

Ejemplo:

class MyResource implements AutoCloseable{ 
    @Override 
    public void close() throws Exception { 
    throw new Exception("Could not close"); 
    } 
    public void read() throws Exception{  
    } 
} 

//this method prints an exception "Could not close" 
//I want to ignore it 
public static void test(){ 
    try(MyResource r = new MyResource()){ 
    r.read(); 
    } catch (Exception e) { 
    System.out.println("Exception: " + e.getMessage()); 
    } 
} 

O debería seguir cerrando en un finally en su lugar?

public static void test2(){ 
    MyResource r = null; 
    try { 
    r.read(); 
    } 
    finally{ 
    if(r!=null){ 
     try { 
     r.close(); 
     } catch (Exception ignore) { 
     } 
    } 
    } 
} 

Respuesta

22

yo encontramos este contestó a la lista de correo de la moneda-dev: http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001503.html

5. Algunos fallos del método de cierre puede ser ignorado con seguridad (por ejemplo, cerrar un archivo que estaba abierto para lectura) . ¿El constructo proporciona esto?

No. Aunque esta funcionalidad parece atractiva, no está claro que valga la pena la complejidad añadida.Como cuestión práctica, estas "excepciones inofensivas " rara vez ocurren, por lo que un programa no será más robusto si se ignoran estas excepciones. Si usted siente que debe ignorarlos, hay una solución, pero no es bastante:

static void copy(String src, String dest) throws IOException { 
    boolean done = false; 
    try (InputStream in = new FileInputStream(src)) { 
     try(OutputStream out = new FileOutputStream(dest)) { 
      byte[] buf = new byte[8192]; 
      int n; 
      while ((n = in.read(buf)) >= 0) 
       out.write(buf, 0, n); 
     } 
     done = true; 
    } catch(IOException e) { 
     if (!done) 
      throw e; 
    } 
} 
16

Se puede usar un patrón decorador aquí para cerrar el recurso en silencio:

public class QuietResource<T extends AutoCloseable> implements AutoCloseable{ 
    T resource; 
    public QuietResource(T resource){ 
     this.resource = resource; 
    } 
    public T get(){ 
     return resource; 
    } 
    @Override 
    public void close() { 
     try { 
      resource.close(); 
     }catch(Exception e){ 
      // suppress exception 
     } 
    } 
} 

no soy personalmente un fan de la sintaxis resultante, pero tal vez esto funciona para usted:

public static void test(){ 
    try(QuietResource<MyResource> qr = new QuietResource<>(new MyResource())){ 
     MyResource r = qr.get(); 
     r.read(); 
    } catch (Exception e) { 
     System.out.println("Exception: " + e.getMessage()); 
    } 
} 

Puede mejorar si está dispuesto a limitarse a manejar interfaces y aprovechar un Dynamic Proxy Cl culo:

public class QuietResource<T> implements InvocationHandler { 

    private T resource; 

    @SuppressWarnings("unchecked") 
    public static <V extends AutoCloseable> V asQuiet(V resource){ 
     return (V) Proxy.newProxyInstance(
       resource.getClass().getClassLoader(), 
       resource.getClass().getInterfaces(), 
       new QuietResource<V>(resource)); 
    } 

    public QuietResource(T resource){ 
     this.resource = resource; 
    } 

    @Override 
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { 
     if(m.getName().equals("close")){ 
      try { 
       return m.invoke(resource, args); 
      }catch(Exception e){ 
       System.out.println("Suppressed exception with message: " + e.getCause().getMessage()); 
       // suppress exception 
       return null; 
      } 
     } 
     return m.invoke(resource, args); 
    } 
} 

A continuación, suponiendo que tiene:

public interface MyReader extends AutoCloseable{ 
    int read(); 
} 

Con una clase real de recursos:

public class MyResource implements MyReader { 

    public void close() throws Exception{ 
     throw new Exception("ha!"); 
    } 

    public int read(){ 
     return 0; 
    } 
} 

Calling sintaxis sería así:

public static void test(){ 
    try(MyReader r = QuietResource.asQuiet(new MyResource())){ 
     r.read(); 
    } catch (Exception e) { 
     System.out.println("Exception: " + e.getMessage()); 
    } 
} 

Puede hacerlo mejor si desea comenzar a incluir bibliotecas, como habilitadores de AOP. Estas soluciones, sin embargo, funcionarán de manera inmediata con JDK7 y sin otras dependencias.

+0

Debe eliminar '' lanza Exception' de cierre() 'para documentar la excepción apresado y simplificar el uso . –

+1

@Mark Elliot Usted _puede implementar el método de interfaz declarado con throws con el método de clase no declarado ningún throws. – BegemoT

4

Esta es una solución:

boolean ok=false; 
    try(MyResource r = new MyResource()) 
    { 
     r.read(); 
     ok=true; 
    } 
    catch (Exception e) 
    { 
     if(ok) 
      ; // ignore 
     else 
      // e.printStackTrace(); 
      throw e; 
    } 

Si ok==true y conseguimos una excepción, que sin duda proviene de close().

Si ok==false, e viene de read() o constructor. close() seguirá siendo llamado y puede arrojar e2, pero e2 será suprimido de todos modos.

El código es bastante legible sin pasar por dicho análisis. Intuitivamente dice, si ok==true, nuestro trabajo real está hecho, y realmente no nos importa qué errores vienen después con respecto al recurso.

Cuestiones relacionadas