39

我想捕获一个异常,它嵌套在另一个异常中。我目前正在这样做:

} catch (RemoteAccessException e) {
    if (e != null && e.getCause() != null && e.getCause().getCause() != null) {
        MyException etrp = (MyException) e.getCause().getCause();
        ...
    } else {
        throw new IllegalStateException("Error at calling service 'service'");
    }
}

有没有办法更高效、更优雅地做到这一点?

4

9 回答 9

31

ExceptionUtils#getRootCause()方法在这种情况下可以派上用场。

于 2011-09-16T05:49:11.890 回答
29

没有更优雅的方式选择性地“捕获”嵌套异常。我想如果你经常捕获这种嵌套异常,你可能会将代码重构为一个通用的实用方法。但它仍然不会优雅或高效。

优雅的解决方案是取消异常嵌套。要么一开始就不要链接异常,要么(有选择地)展开并重新抛出嵌套的异常,使其进一步向上堆栈。

异常倾向于嵌套的原因有 3 个:

  1. 您已决定原始异常的详细信息不太可能对应用程序的错误恢复有用……但您希望保留它们以用于诊断目的。

  2. 您正在实现不允许特定已检查异常的 API 方法,但您的代码不可避免地会引发该异常。一个常见的解决方法是在未经检查的异常中“走私”检查的异常。

  3. 您很懒惰,将一组不同的不相关异常转换为单个异常,以避免在您的方法签名1中有大量已检查异常。

在第一种情况下,如果您现在需要区分包装的异常,那么您最初的假设是不正确的。最好的解决方案是更改方法签名,以便您可以摆脱嵌套。

在第二种情况下,您可能应该在控制权通过有问题的 API 方法后立即解开异常。

在第三种情况下,你应该重新考虑你的异常处理策略;即正确地做2


1 - 事实上,由于在 Java 7 中引入了多异常捕获语法,这样做的半合法原因之一已经消失。

2 - 不要将您的 API 方法更改为throws Exception. 那只会让事情变得更糟。Exception您现在必须在每次调用方法时“处理”或传播。这是癌症...

于 2010-06-02T07:19:16.257 回答
24

您应该添加一些检查以查看是否e.getCause().getCause()真的是MyException. 否则此代码将抛出一个ClassCastException. 我可能会这样写:

} catch(RemoteAccessException e) {
    if(e.getCause() != null && e.getCause().getCause() instanceof MyException) {
        MyException ex = (MyException)e.getCause().getCause();
        // Do further useful stuff
    } else {
        throw new IllegalStateException("...");
    }
}
于 2010-06-02T06:36:07.530 回答
7

我刚刚通过编写一个简单的实用程序方法解决了这样的问题,它将检查整个引起链。

  /**
   * Recursive method to determine whether an Exception passed is, or has a cause, that is a
   * subclass or implementation of the Throwable provided.
   *
   * @param caught          The Throwable to check
   * @param isOfOrCausedBy  The Throwable Class to look for
   * @return  true if 'caught' is of type 'isOfOrCausedBy' or has a cause that this applies to.
   */
  private boolean isCausedBy(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
    if (caught == null) return false;
    else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return true;
    else return isCausedBy(caught.getCause(), isOfOrCausedBy);
  }

当您使用它时,您只需创建一个从最具体的异常到最不具体的 if 列表,并带有一个备用 else 子句:

try {
  // Code to be executed
} catch (Exception e) {
  if (isCausedBy(e, MyException.class)) {
    // Handle MyException.class
  } else if (isCausedBy(e, AnotherException.class)) {
    // Handle AnotherException.class
  } else {
    throw new IllegalStateException("Error at calling service 'service'");
  }
}

评论中每个请求的替代/添加

如果您想使用类似的方法来获取您要查找的类的 Exception 对象,您可以使用类似这样的方法:

  private boolean getCausedByOfType(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
    if (caught == null) return null;
    else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return caught;
    else return getCausedByOfType(caught.getCause(), isOfOrCausedBy);
  }

除了这种方式之外,还可以使用isCausedBy()它:

  if (isCausedBy(e, MyException.class)) {
    Throwable causedBy = getCausedBy(e, MyException.class);
    System.err.println(causedBy.getMessage());
  }

它也可以直接使用而不是isCausedBy(),尽管这是否更具可读性可能是一个见仁见智的问题。

  Throwable causedBy;
  if ((causedBy = getCausedBy(e, IllegalAccessException.class)) != null) {
    System.err.println(causedBy.getMessage());
  }
于 2018-07-19T19:45:02.203 回答
2

我看不出你为什么希望异常处理高效而优雅,我满足于有效。出于某种原因,它们被称为异常。

此代码将是维护的噩梦。你不能重新设计调用堆栈来抛出你感兴趣的异常吗?如果它很重要,方法签名应该显示它,而不是隐藏它,包裹在其他 2 个异常中。

第一个 (e != null) 是不必要的。

您可以将第三个更好地更改为 e.getCause().getCause() instanceof MyException)

于 2010-06-02T06:41:39.533 回答
1

您可以执行以下操作:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}
于 2016-05-06T16:32:11.783 回答
0

我怀疑,但您可以检查instanceof异常是否属于正确类型。

编辑:嵌套异常被包装应该是有原因的,所以你必须问自己捕获嵌套异常的目的是什么。

于 2010-06-02T06:33:23.293 回答
0

如果您正在调查异常是否由自定义异常(例如MyException)引起,您可以使用 while 循环进行迭代,直到找到MyException.

boolean isCausedByMyException(Throwable exception) {
    do {
        if (exception instanceof MyException) {
            return true;
        }

        exception = exception.getCause();
    } while (exception != null);

    return false;
}
于 2021-10-14T12:12:50.687 回答
-1

我想你也可以ExceptionUtils.throwableOfThrowable()这里使用

于 2020-12-12T04:19:43.643 回答