有没有可能将多个异常收集到一个自定义异常中?

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

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,您可能希望创建许多异常,例如 UserNotFoundExceptionIncorrectPasswordExceptionUserFrozenExceptionIpBlockedException 等等。

如果您确保所有这些异常都扩展自一个单一的异常类型(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

ClassNotFoundExceptionNoSuchMethodExceptionIllegalAccessExceptionInvocationTargetException 都是 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.

huangapple
  • 本文由 发表于 2020年8月31日 18:57:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/63669500.html
匿名

发表评论

匿名网友

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

确定