我正在尝试创建一个帮助方法,它不需要像这样的代码:
void foo() throws ExceptionA, ExceptionB, DefaultException {
try {
doSomething(); // that throws ExceptionA, ExceptionB or others
} catch (Exception e) {
if (e instanceof ExceptionA)
throw new ExceptionA("extra message", e);
if (e instanceof ExceptionB)
throw new ExceptionB("extra message", e);
throw new DefaultException("extra message", e);
}
}
问题是我需要同时在函数声明和函数体中维护throws列表。我正在寻找如何避免这种情况,并使更改投掷列表足够,我的代码看起来像:
void foo() throws ExceptionA, ExceptionB, DefaultException {
try {
doSomething(); // that throws ExceptionA, ExceptionB or others
} catch (Exception e) {
rethrow(DefaultException.class, "extra message", e);
}
}
其中rethrow方法足够智能,可以从方法声明中识别出throws列表。
这样,当我更改我的方法在throws列表中传播的类型列表时,我不需要更改正文。
以下是可以解决问题的功能。问题是因为它不知道它将抛出什么类型的异常它的抛出声明必须说Exception,但如果它这样做,那么将要使用它的方法也需要指定它,并且整个想法使用投掷列表进入地狱。
有关如何解决这个问题的任何建议吗?
@SuppressWarnings("unchecked")
public static void rethrow(Class<?> defaultException, String message, Exception e) throws Exception
{
final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
final StackTraceElement element = ste[ste.length - 1 - 1];
Method method = null;
try {
method = getMethod(element);
} catch (ClassNotFoundException ignore) {
// ignore the Class not found exception - just make sure the method is null
method = null;
}
boolean preserveType = true;
if (method != null) {
// if we obtained the method successfully - preserve the type
// only if it is in the list of the thrown exceptions
preserveType = false;
final Class<?> exceptions[] = method.getExceptionTypes();
for (Class<?> cls : exceptions) {
if (cls.isInstance(e)) {
preserveType = true;
break;
}
}
}
if (preserveType)
{
// it is throws exception - preserve the type
Constructor<Exception> constructor;
Exception newEx = null;
try {
constructor = ((Constructor<Exception>) e.getClass().getConstructor());
newEx = constructor.newInstance(message, e);
} catch (Exception ignore) {
// ignore this exception we prefer to throw the original
newEx = null;
}
if (newEx != null)
throw newEx;
}
// if we get here this means we do not want, or we cannot preserve the type
// just rethrow it with the default type
Constructor<Exception> constructor;
Exception newEx = null;
if (defaultException != null) {
try {
constructor = (Constructor<Exception>) defaultException.getConstructor();
newEx = constructor.newInstance(message, e);
} catch (Exception ignore) {
// ignore this exception we prefer to throw the original
newEx = null;
}
if (newEx != null)
throw newEx;
}
// if we get here we were unable to construct the default exception
// there lets log the message that we are going to lose and rethrow
// the original exception
log.warn("this message was not propagated as part of the exception: \"" + message + "\"");
throw e;
}
更新1:
我可以使用RuntimeException
来避免抛出声明的需要,但在这种情况下,我正在丢失异常的类型,这是最重要的一点。
想法如何解决这个问题?
答案 0 :(得分:3)
我猜测你正在做实际工作的代码(即你没有修改异常的部分)就像这样。
public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
try
{
// some code that could throw ExceptionA
...
// some code that could throw OtherExceptionA
...
// some code that could throw ExceptionB
...
// some code that could throw OtherExceptionB
}
catch (Exception e)
{
if( e instanceof ExceptionA )
{
throw new ExceptionA("extra message", e);
}
if( e instanceof ExceptionB )
{
throw new ExceptionB("extra message", e);
}
throw new DefaultException("extra message", e);
}
}
有两种更好的方法
第一种方法
public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
// some code that could throw ExceptionA
...
try
{
// some code that could throw OtherExceptionA
...
}
catch (Exception e)
{
throw new DefaultException("extra message", e);
}
// some code that could throw ExceptionB
...
try
{
// some code that could throw OtherExceptionB
}
catch (Exception e)
{
throw new DefaultException("extra message", e);
}
}
第二种方法
public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
try
{
// some code that could throw ExceptionA
...
// some code that could throw OtherExceptionA
...
// some code that could throw ExceptionB
...
// some code that could throw OtherExceptionB
}
catch (OtherExceptionA | OtherExceptionB e)
{
throw new DefaultException("extra message", e);
}
}
第一种方法是好的,如果你想不惜一切代价继续执行,如果你遇到它们就抓住并包装RuntimeException
。一般来说,你不想做这件事,让它们传播起来会更好,因为你可能无法处理它们。
第二种方法通常是最好的。在这里,您明确指出可以处理哪些异常,并通过包装它们来处理它们。意外的RuntimeException
会传播,除非你有办法处理它们。
只是一般性评论:与StackTraceElement
一起玩并不是一个好主意。您最终可能会从Thread.currentThread().getStackTrace()
获取一个空数组(尽管如果使用现代Oracle JVM,您很可能不会这样做),并且调用方法的深度始终不是length-2
,它可能会是length-1
,特别是在旧版本的Oracle JVM中。
您可以在this question中了解有关此问题的更多信息。
答案 1 :(得分:0)
详细说明一些人)告诉你的,这是MyFunctionFailedException,当然它应该被命名为更合理的东西:
public class MyFunctionFailedException extends Exception {
public MyFunctionFailedException(String message, Throwable cause) {
super(message, cause);
}
}
然后你的catch块会变成这样的东西。
try {
...
} catch (Exception e) {
throw new MyFunctionFailedException("extra message", e);
}
如果您确实想要重新抛出较低级别的异常,则应使用多个catch块。请注意,并非所有类型的异常都必须具有允许您添加原因的构造函数。而且你真的应该考虑为什么你的方法让一个未被捕获的SQLException冒泡到调用堆栈是有意义的。