捕获嵌套在另一个异常中的异常

发布于 2024-09-04 08:35:35 字数 400 浏览 7 评论 0原文

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

} 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'");
    }
}

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

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'");
    }
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

凉世弥音 2024-09-11 08:35:36

ExceptionUtils#getRootCause() 方法在这种情况下会非常方便。

The ExceptionUtils#getRootCause() method can come in very handy in such situations.

七颜 2024-09-11 08:35:36

没有比这更优雅的方法来选择性地“捕获”嵌套异常了。我想如果您经常捕获这种嵌套异常,您可能会将代码重构为通用实用程序方法。但它仍然既不优雅也不高效。

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

异常往往因 3 个原因而被嵌套:

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

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

  3. 您太懒了,将一组不同的不相关异常转换为单个异常,以避免方法签名中出现大量已检查异常1

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

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

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


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

2 - 不要更改您的 API方法抛出异常。这只会让事情变得更糟。现在,每次调用方法时都必须“处理”或传播Exception。这是一种癌症......

There is no more elegant way of selectively "catching" nested exceptions. I suppose if you did this kind of nested exception catching a lot, you could possibly refactor the code into a common utility method. But it still won't be either elegant or efficient.

The elegant solution is to do away with the exception nesting. Either don't chain the exceptions in the first place, or (selectively) unwrap and rethrow the nested exceptions further up the stack.

Exceptions tend to be nested for 3 reasons:

  1. You have decided that the details of the original exception are unlikely to be useful for the application's error recovery ... but you want to preserve them for diagnostic purposes.

  2. You are implementing API methods that don't allow a specific checked exception but your code unavoidably throws that exception. A common workaround is to "smuggle" the checked exception inside an unchecked exception.

  3. You are being lazy and turning a diverse set of unrelated exceptions into a single exception to avoid having lots of checked exceptions in your method signature1.

In the first case, if you now need to discriminate on the wrapped exceptions, then your initial assumptions were incorrect. The best solution is change method signatures so that you can get rid of the nesting.

In the second case, you probably should unwrap the exceptions as soon as control has passed the problematic API method.

In the third case, you should rethink your exception handling strategy; i.e. do it properly2.


1 - Indeed, one of the semi-legitimate reasons for doing this has gone away due to the introduction of the multi-exception catch syntax in Java 7.

2 - Don't change your API methods to throws Exception. That only makes things worse. You now have to either "handle" or propagate Exception each time you call the methods. It is a cancer ...

我是男神闪亮亮 2024-09-11 08:35:36

您应该添加一些检查来查看 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("...");
    }
}

You should add some checks to see if e.getCause().getCause() is really a MyException. Otherwise this code will throw a ClassCastException. I would probably write this like:

} 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("...");
    }
}
鹿港小镇 2024-09-11 08:35:36

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

  /**
   * 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 Throwable 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 = getCausedByOfType(e, MyException.class);
    System.err.println(causedBy.getMessage());
  }

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

  Throwable causedBy;
  if ((causedBy = getCausedByOfType(e, IllegalAccessException.class)) != null) {
    System.err.println(causedBy.getMessage());
  }

I just solved a problem like this by writing a simple utility method, which will check the entire caused-by chain.

  /**
   * 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);
  }

When you use it, you would just create a list of if's from most specific Exception to least specific, with a fallback else-clause:

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'");
  }
}

Alternative/Addition per requests in comments

If you want to use a similar method to get the Exception object of the class you're looking for, you can use something like this:

  private Throwable 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);
  }

This could be used in addition to isCausedBy() this way:

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

It can also used directly instead of isCausedBy(), although it's probably a matter of opinion whether this is more readable.

  Throwable causedBy;
  if ((causedBy = getCausedByOfType(e, IllegalAccessException.class)) != null) {
    System.err.println(causedBy.getMessage());
  }
秋心╮凉 2024-09-11 08:35:36

我不明白为什么你希望异常处理高效且优雅,我满足于有效。它们被称为例外是有原因的。

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

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

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

I see no reason why you want exception handling to be efficient and elegant, I settle for effective. They're called Exceptions for a reason.

This code will be a maintenance nightmare. Can't you redesign the call stack to throw the Exception you are interested in? If it is important the method signatures should show it and not hide it wrapped in 2 other exceptions.

The first (e != null) is unnecessary.

And you can change the 3rd better to e.getCause().getCause() instanceof MyException)

猫七 2024-09-11 08:35:36

您可以执行以下操作:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}

You can do as below:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}
南薇 2024-09-11 08:35:36

我想您也可以使用 ExceptionUtils.throwableOfThrowable() ,如 此处

I suppose you could also use ExceptionUtils.throwableOfThrowable() as in here

暗藏城府 2024-09-11 08:35:36

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

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

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

    return false;
}

If you are investigating that whether an exception is caused by a custom exception (e.g. MyException) you can iterate with a while-loop until you find an instance of MyException.

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

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

    return false;
}
昔日梦未散 2024-09-11 08:35:36

我对此表示怀疑,但您可以使用 instanceof 检查异常的类型是否正确。

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

I doubt, but you can check with instanceof if the exception is of the correct type.

Edit: There should be a reason that the nested exception is wrapped, so you have to ask yourself what is the purpose of catching the nested one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文