web-dev-qa-db-ja.com

try-with-resourcesを使用してリソースを静かに閉じます

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

私はこれが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;
    }
}
25
dogbane

ここでデコレータパターンを使用して、リソースを静かに閉じることができます。

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を使用するだけですぐに機能し、他の依存関係はありません。

19
Mark Elliot

これは1つの解決策です:

_    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_の場合、eread()またはコンストラクターから取得されます。 close()は引き続き呼び出され、_e2_をスローする可能性がありますが、e2はとにかく抑制されます。

このような分析を行わなくても、コードは非常に読みやすくなります。直感的には、_ok==true_の場合、実際の作業は完了しており、その後にリソースに関してどのようなエラーが発生するかは気にしません。

4
irreputable