英文:
Is it possible to gather multiple exceptions into one custom exception?
问题
因为重构的原因,我需要使用反射 API 抛出反复出现的异常:
throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
在我使用重构后的方法的地方,这些异常始终存在,是否有可能像这样做:
throws CustomException
以处理上述所有提到的异常?谢谢
英文:
I have recurring exceptions that need to be thrown due to using the reflect API for refactoring reasons:
throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
These exceptions are always present in the method where I use the refactored methods, Is it possible to do
something like this instead:
throws CustomException
to handle all of the exceptions mentioned above?
Thanks
答案1
得分: 2
是的。有两种方法可以做到这一点:
类层次结构
这个方法只适用于您设计异常的情况。如果这不适用于您的情况,请继续阅读下一章节。
例如,如果您有一个用户认证 API,您可能希望创建许多异常,例如 UserNotFoundException
、IncorrectPasswordException
、UserFrozenException
、IpBlockedException
等等。
如果您确保所有这些异常都扩展自一个单一的异常类型(AuthenticationException
),如下所示:
public class AuthenticationException extends Exception {
public AuthenticationException(String msg, Throwable cause) {
super(msg, cause);
}
public AuthenticationException(String msg) {
super(msg);
}
}
public class UserNotFoundException extends AuthenticationException {
public UserNotFoundException(String username) {
super(username);
}
}
// 其他类类似
那么调用者就可以进行选择,这就是为什么这样的设计非常好。
如果调用者需要根据发生的身份验证问题的类型具有自定义的反应,他们可以捕获 UserNotFoundException
。但如果他们不特别关心,他们可以捕获 AuthenticationException
。
注意,Java 本身也是这样做的。GeneralSecurityException 是常见的超类,但大多数与安全相关的方法会抛出许多这个类的子类,这样调用者就知道可能发生了什么类型的错误,并且可以为特定的错误编写处理程序,而不会强制它们编写大量的捕获块,如果他们不想的话。
重新抛出异常
另一种解决方案是将异常捆绑到您自己的自定义类型中。
要做到这一点,首先编写一个异常(或使用现有的异常,但从风格上来说,这很少是正确的选择)。然后,编写一个捕获块,将所有这些奇怪的、不相关的异常捆绑到这个新创建的异常中,确保使用 'cause' 系统:
public class CustomException { // 找一个更好的名称
public CustomException(String msg, Throwable cause) {
super(msg, cause);
}
public CustomException(String msg) {
super(msg);
}
public CustomException(Throwable cause) {
super(cause);
}
}
public void myMethod() throws CustomException {
try {
// 在这里编写代码
} catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
throw new CustomException(e);
} catch (InvocationTargetException e) {
throw new CustomException(e.getCause());
}
}
但要注意,您表达的只想“点火并忘记”的建议会导致您的 API 设计变得有问题。例如,像这样包装 InvocationTargetException 对于您的 API 消费者来说相当不友好;InvocationTargetException 本身就是一个包装器,所以现在你有了包装器中的包装器中的包装器,这样的代码看起来非常丑陋,并且使得调用代码难以精确处理问题。实际上,没有一种简单的方法可以避免阅读您使用的 API 并理解它为什么会抛出各种异常。
注意:如果可能的话,类层次结构解决方案是更好的方法;但这确实要求您能够控制抛出异常的代码。
英文:
Yes. There are two ways to do this:
Class hierarchies
This one only applies if you are the one designing the exceptions. If that doesn't describe this case, move on to the next chapter.
If you have, say, a user authentication API, you would presumably want to create a whole bevy of exceptions such as UserNotFoundException
, IncorrectPasswordException
, UserFrozenException
, IpBlockedException
, and more.
If you make sure all those exceptions extend a single exception type (AuthenticationException
), like so:
public class AuthenticationException extends Exception {
public AuthenticationException(String msg, Throwable cause) {
super(msg, cause);
}
public AuthenticationException(String msg) {
super(msg);
}
}
public class UserNotFoundException extends AuthenticationException {
public UserNotFoundException(String username) {
super(username);
}
}
// and so on
then a caller can choose, which is why this makes for such a great API design.
IF a caller needs to have custom reactions depending on what kind of authentication problem occurs, they can catch, say, UserNotFoundException
. But if they don't particularly care, they can catch AuthenticationException
.
Note that java itself does this, too. GeneralSecurityException is the common superclass, and yet most security-related methods throw a sizable list of subclasses of this, so that callers know exactly what kind of errors can occur, and can write handlers for specific errors, without forcing them to write a ton of catch blocks if they don't want to.
rethrow
An alternate solution is that you bundle up exceptions into your own custom type.
To do this, first write an exception (or use an existing one, but that's rarely stylistically the right move). Then, write a catch block and wrap all those weird, unrelated exceptions into this newly created one, making sure to use the 'cause' system:
public class CustomException { // find a better name
public CustomException(String msg, Throwable cause) {
super(msg, cause);
}
public CustomException(String msg) {
super(msg);
}
public CustomException(Throwable cause) {
super(cause);
}
}
public void myMethod() throws CustomException {
try {
// code here
} catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
throw new CustomException(e);
} catch (InvocationTargetException e) {
throw new CustomException(e.getCause();
}
}
but note how your suggestion that you just want to fire-and-forget is causing your API design to be problematic. For example, wrapping an InvocationTargetException like this is rather hostile to your API consumers; InvocationTargetException is itself a wrapper, so now you've got wrappers-in-wrappers-in-wrappers and that's just ugly and makes it real hard for calling code to even try to deal with problems in a pinpoint fashion. There really is no easy way around reading the APIs you use and understanding why it throws the various exceptions that it throws.
NB: If you can, the class hierarchies solution is a much better way to do this; but it does require that you are in control of the code that's throwing them.
答案2
得分: 0
假设你有:
void myMethod() throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
/// 你的代码在这里;
}
你可以使用以下方式进行更改:
void myMethod() throws CustomException {
try {
/// 你的代码在这里;
} catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new CustomException(e); // 确保构造函数初始化时带有原因(cause)。
}
}
例如,CustomException 类:
public class CustomException {
CustomException(Throwable cause) {
super(cause);
}
}
英文:
Suppose you have:
void myMethod() throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
/// Your code here;
}
You can change it with:
void myMethod() throws CustomException {
try {
/// Your code here;
} catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new CustomException(e); // Make sure to have constructor initialized with cause.
}
}
E.g. CustomException class:
public class CustomException {
CustomException(Throwable cause) {
super(cause);
}
}
答案3
得分: 0
ClassNotFoundException
、NoSuchMethodException
、IllegalAccessException
和 InvocationTargetException
都是 ReflectiveOperationException
的子类,因此您可以简化异常列表:
throws ApiException, ReflectiveOperationException
如果您想将这两者合并为一个 CustomException
,只需创建一个并使用它:
public class CustomException extends Exception {
private static final long serialVersionUID = -1006015659738896046L;
public CustomException(Exception e) {
super(e);
}
public CustomException(String msg, Exception e) {
super(msg, e);
}
}
void foo() throws CustomException {
try {
// 在此处添加您的代码
} catch (ApiException | ReflectiveOperationException e) {
throw new CustomException(e);
}
}
如果您甚至不想使用 throws CustomException
,因为您知道调用者永远不会专门捕获它,将异常类更改为 CustomException extends RuntimeException
。
英文:
ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
are all subclasses of ReflectiveOperationException
, so you can simply reduce the exception list:
throws ApiException, ReflectiveOperationException
If you want to combine those 2 into a CustomException
, just create one and use it:
public class CustomException extends Exception {
private static final long serialVersionUID = -1006015659738896046L;
public CustomException(Exception e) {
super(e);
}
public CustomException(String msg, Exception e) {
super(msg, e);
}
}
void foo() throws CustomException {
try {
// your code here
} catch (ApiException | ReflectiveOperationException e) {
throw new CustomException(e);
}
}
If you don't even want the throws CustomException
, because you know the caller will never specifically catch it, change exception class to CustomException extends RuntimeException
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论