37

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

4 に答える 4

29

これは 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;
    }
}
于 2011-07-31T14:49:19.100 に答える
23

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

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 でそのまま使用でき、他の依存関係はありません。

于 2011-07-31T13:27:53.327 に答える
5

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

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

于 2011-07-31T13:55:13.740 に答える