避免在finally块可能引发异常时隐藏异常。

huangapple go评论89阅读模式
英文:

Avoiding exception hiding when finally block could throw

问题

Here's the translated content:

当你有一个包装在try-catch-finally中的进程,并且最终的操作可能会引发异常时,如何处理异常隐藏?

这是一个问题和可能的修复示例。修复看起来是否可以,或者我是不是处理异常不当?

具有隐藏问题的代码

try {
  // try块
  setupTestEnv();
  runTests();
} finally {
  // finally块
  cleanupTestEnv();
}
  • try块引发的异常仍然会导致清理并沿着调用堆栈抛出
  • 在try块引发异常的情况下,如果finally块也引发异常,那么try块引发的异常会被隐藏或覆盖,因为finally块引发的异常会沿着调用堆栈传播。

可能的解决方法

改编自https://stackoverflow.com/a/41246027/9950

Throwable tryBlockException;
try {
  setupTestEnv();
  runTests();
} catch (Throwable t) {
  // 记录并抛出异常
  tryBlockException = t;
  throw t;
} finally {
   try {
      cleanupTestEnv();
   } catch (Throwable t) {
      if (tryBlockException != null) {
         // 我们有来自try块的异常
         // 这是优先级更高的异常

         // 将清理异常添加为被抑制的异常
         // 它将在对象和调用报告中显示
         tryBlockException.addSuppressed(t);
         throw tryBlockException;
      } else {
         throw t;
      }
  }
}
英文:

When you have a process which you’ve wrapped in a try catch, finally, and the action in the final could possibly throw, how do you handle exception, hiding?

Here's an example of a problem and a possible fix. Does the fix look ok, or am I re-throwing poorly?

Code with Hiding Problem

try {
  // try-block
  setupTestEnv();
  runTests();
} finally {
  // finally-block
  cleanupTestEnv();
}
  • try-block throws still result in cleanup and get thrown up the call stack
  • try-block throws where finally-block throws as well result in the loss or hiding of try-block throw as finally-block throw goes up the call stack.

Possible Workaround

Adapted from https://stackoverflow.com/a/41246027/9950

Throwable tryBlockException;
try {
  setupTestEnv();
  runTests();
} catch (Throwable t) {
  // record and throw
  tryBlockException = t;
  throw ex;
} finally {
   try {
      cleanupTestEnv()
   } catch (Throwable t) {
      if (tryBlockException != null) {
         // we have an exception from try-block
         // and that's priority exception

         // add the cleanup exception as suppressed
         // it'll be in the object and call reporting
         tryBlockException.addSuppressed(t);
         throw tryBlockException;
      } else {
         throw t;
      }
  }
}

答案1

得分: 1

以下是翻译好的部分:

"Looks fine to me. I would suggest putting the bulk of the code into a re-usable method, e.g.:

public interface Attempt {
void apply() throws Exception;
}

public static void tryCatchFinally(
Attempt mainF,
Attempt finallyF
) throws Exception {
Exception tryCatchException = null;
try {
mainF.apply();
} catch (Exception ex) {
tryCatchException = ex;
throw ex;
} finally {
try {
finallyF.apply();
} catch (Exception ex) {
if (tryCatchException != null) {
tryCatchException.addSuppressed(ex);
}
}
}
}

then you can just use it like this:

tryCatchFinally(
() -> {setupTestEnv(); runTests();},
() -> cleanupTestEnv()
);"

请告诉我您是否需要进一步的帮助。

英文:

Looks fine to me. I would suggest putting the bulk of the code into a re-usable method, e.g.:

public interface Attempt {
    void apply() throws Exception;
}

public static void tryCatchFinally(
        Attempt mainF,
        Attempt finallyF
) throws Exception {
    Exception tryCatchException = null;
    try {
        mainF.apply();
    } catch (Exception ex) {
        tryCatchException = ex;
        throw ex;
    } finally {
        try {
            finallyF.apply();
        } catch (Exception ex) {
            if (tryCatchException != null) {
                tryCatchException.addSuppressed(ex);
            }
        }
    }
}

then you can just use it like this:

tryCatchFinally(
        () -> {setupTestEnv(); runTests();},
        () -> cleanupTestEnv()
);

答案2

得分: 0

这是一种方法,除了在您的情况下,不是"t",而是"tryblockexception"被抑制了,您不需要重新抛出"tryblockexception"。

其他选项包括使用"try-with-resources"来自动关闭类型,或者在可能的情况下确保"finally"子句不会抛出异常,因为它可能会吞噬"try"块中的异常。

英文:

That's a way to go except that in your case it's not t but tryblockexception which is suppressed and you don't need to rethrow tryblockexception neither.

Other options are try-with-resources for autocloseable type or when possible making sure that the finally clause does not throw ex because it can swallow exceptions from the try block.

答案3

得分: 0

我不确定为什么你要以这种复杂的方式使用finally。你也可以只使用在try-catch块之后运行的代码:

Exception priorityException;
try {
  setupTestEnv();
  runTests();
} catch (Exception ex) {
  priorityException = ex;  // 延迟处理
}

try {
  cleanupTestEnv();
} catch (Exception ex) {
  if (priorityException != null) {
    priorityException.addSuppressed(ex);
  } else {
    priorityException = ex;
  }
  throw priorityException;
}

请注意,我使用了catch (Exception ...)而不是你的Throwable,因为通常无法以有用的方式捕获和处理Error。查看Stack Overflow问题Catching Java errors以及其中的答案,以获得更好的了解。

英文:

I'm not sure why you have to use a finally in such a complex way. You could also just use code that runs after the try-catch block:

Exception priorityException;
try {
  setupTestEnv();
  runTests();
} catch (Exception ex) {
  priorityException = ex;  // delayed handling
}

try {
  cleanupTestEnv();
} catch (Exception ex) {
  if (priorityException != null) {
    priorityException.addSuppressed(ex);
  } else {
    priorityException = ex;
  }
  throw priorityException;
}

Please note that I used a catch (Exception ...) instead of your Throwable because catching and handling Errors is usually not possible in a useful way. See SO question Catching Java errors and its answers for better insight.

huangapple
  • 本文由 发表于 2023年8月5日 00:26:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定