Тихо закройте ресурс, используя try-with-resources - PullRequest
27 голосов
/ 31 июля 2011

Можно ли игнорировать исключение, генерируемое при закрытии ресурса, с помощью оператора try-with-resources?

Пример:

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

Или я должен продолжать закрываться вfinally вместо?

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

Ответы [ 3 ]

25 голосов
/ 31 июля 2011

Я нашел ответ в списке рассылки coin-dev: http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001503.html

5. Некоторые ошибки метода close можно безопасно игнорировать (например, закрытие файла, который был открыт для чтения). Предусматривает ли конструкция это?

Нет. Хотя эта функциональность кажется привлекательной, неясно, что это стоит дополнительной сложности. На практике эти «безобидные исключения »редко, если когда-либо происходят, поэтому программа не будет более надежной если эти исключения игнорируются. Если вы чувствуете, что должны игнорировать их, есть обходной путь, но он не очень хорош:

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;
    }
}
19 голосов
/ 31 июля 2011

Вы можете использовать шаблон декоратора для тихого закрытия ресурса:

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

Лично я не фанат получающегося синтаксиса, но, возможно, это работает для вас:

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

Вы можете добиться большего успеха, если хотите ограничиться работой с интерфейсами и использовать класс динамического прокси:

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

Тогда, если у вас есть:

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

С фактическим классом ресурса:

public class MyResource implements MyReader {

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

    public int read(){
        return 0;
    }
}

Синтаксис вызова будет выглядеть так:

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

Вы можете добиться большего успеха, чем это, если вы хотите начать включать библиотеки, такие как активаторы AOP. Однако эти решения будут работать из коробки с JDK7 и без других зависимостей.

4 голосов
/ 31 июля 2011

Это одно из решений:

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

Если ok==true и у нас есть исключение, оно определенно приходит от close().

Если приходит ok==false, eот read() или конструктор.close() будет по-прежнему вызываться и может выдать e2, но e2 все равно будет подавлен.

Код вполне читабелен, не проходя такой анализ.Интуитивно он говорит, что если ok==true, наша реальная работа выполнена, и нам все равно, какие ошибки появятся после этого в отношении ресурса.

...