Catching an exception that is nested into another exception

26 java exception try-catch nested-exceptions

I want to catch an exception, that is nested into another exception. I'm doing it currently this way:

} 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'");
    }
}
Run Code Online (Sandbox Code Playgroud)

Is there a way to do this more efficient and elegant?

Ste*_*n C 26

没有更优雅的方式来选择性地"捕获"嵌套异常.我想如果你做了这种嵌套异常捕获很多,你可以将代码重构为一个通用的实用方法.但它仍然不会优雅或高效.

优雅的解决方案是取消异常嵌套.要么不首先链接异常,要么(有选择地)解包并重新抛出堆栈中的嵌套异常.

例外情况往往嵌套有三个原因:

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

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

  3. 你是懒惰和车削不同的组不相关的例外成一个例外,以避免大量的检查型异常的在你的方法签名1.

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

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

在第三种情况下,您应该重新考虑您的异常处理策略; 即做得好2.


1 - 实际上,由于Java 7中引入了多异常捕获语法,因此实现此目的的半正当理由之一已经消失.

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


小智 20

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


Mar*_*arc 18

你应该添加一些检查,看看是否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("...");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在这种情况下,不需要检查e.getCause().getCause()!= null,因为如果为null,instanceof将返回false. (7认同)

Ric*_*oek 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);
  }
Run Code Online (Sandbox Code Playgroud)

当您使用它时,您只需创建一个从最具体的异常到最不具体的 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'");
  }
}
Run Code Online (Sandbox Code Playgroud)

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

如果你想使用类似的方法来获取你正在寻找的类的 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);
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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

  Throwable causedBy;
  if ((causedBy = getCausedBy(e, IllegalAccessException.class)) != null) {
    System.err.println(causedBy.getMessage());
  }
Run Code Online (Sandbox Code Playgroud)