2011-07-31 13 views
22

Czy można zignorować wyjątek zgłoszony, gdy zasób zostanie zamknięty za pomocą instrukcji try-with-resources?Zamknij zasób po cichu, korzystając z funkcji prób z zasobami.

Przykład:

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

Albo powinienem nadal zamknąć w finally zamiast?

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

Odpowiedz

22

znalazłem ten odpowiedział na liście mailingowej coin-dev: http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001503.html

5. Niektóre awarie ścisłej metody można zignorować (np zamykania pliku, który został otwarty do odczytu) . Czy konstrukcja zapewnia to?

Nie. Chociaż ta funkcja wydaje się atrakcyjna, nie jest jasne, czy jest ona warta dodatkowej złożoności.Praktycznie rzecz biorąc, te "nieszkodliwe" wyjątki "rzadko, jeśli kiedykolwiek wystąpią, więc program nie będzie bardziej solidny, jeśli te wyjątki zostaną zignorowane. Jeśli czujesz, że musisz je zignorować, istnieje obejście, ale to nie jest całkiem:

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

Można użyć wzoru dekorator tutaj, aby zamknąć zasób cicho:

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

osobiście nie jestem fanem powstałego składni, ale być może to działa dla Ciebie:

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

Możesz zrobić lepiej, jeśli chcesz ograniczyć się do obsługi interfejsów i korzystać z Dynamic Proxy Cl ass:

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

Następnie zakładając, że masz:

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

o rzeczywistej klasy zasobów:

public class MyResource implements MyReader { 

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

    public int read(){ 
     return 0; 
    } 
} 

Wywołanie składni wyglądałby następująco:

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

Możesz zrobić lepiej niż to, jeśli chcesz zacząć dodawać biblioteki, takie jak AOP. Rozwiązania te będą jednak działać po wyjęciu z pudełka z JDK7 i żadnymi innymi zależnościami.

+0

Należy usunąć 'rzuca Exception' od' close() 'w celu udokumentowania catched wyjątek i uprościć użytkowanie . –

+1

@ Mark Elliot Możesz _ implementować metodę interfejsu zadeklarowaną przy rzutach z metodą klasy nie zadeklarowaną żadnych rzutów. – BegemoT

4

Jest to jedno rozwiązanie:

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

Jeśli ok==true i mamy wyjątek, to na pewno pochodzi z close().

Jeśli ok==false, e pochodzi od read() lub konstruktora. close() nadal będzie wywoływane i może rzucać e2, ale e2 zostanie w ten sposób zniesione.

Kod jest czytelny bez przechodzenia przez taką analizę. Intuicyjnie mówi, że jeśli ok==true, nasza prawdziwa praca jest wykonywana, i tak naprawdę nie obchodzi nas, jakie błędy pojawiają się po tym, co dotyczy zasobu.

Powiązane problemy