使用新消息重新显示java异常,如果异常类型在方法声明列表中,则保留该异常类型

使用新消息重新显示java异常,如果异常类型在方法声明列表中,则保留该异常类型,java,exception,rethrow,Java,Exception,Rethrow,我正在尝试创建一个帮助器方法,它将消除使用以下代码的需要: void foo() throws ExceptionA, ExceptionB, DefaultException { try { doSomething(); // that throws ExceptionA, ExceptionB or others } catch (Exception e) { if (e instanceof ExceptionA) throw new Except

我正在尝试创建一个帮助器方法,它将消除使用以下代码的需要:

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);
  }
}
问题是我需要在函数声明和函数体中同时维护抛出列表。我正在寻找如何避免这种情况,并使更改抛出列表足够,我的代码看起来像:

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声明必须说exception,但是如果它这样做,将要使用它的方法也需要指定它,使用throws列表的整个想法都会失败

有什么建议可以解决这个问题吗

@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;
}
@SuppressWarnings(“未选中”)
公共静态void rethrow(类defaultException、字符串消息、异常e)引发异常
{
最终StackTraceElement[]ste=Thread.currentThread().getStackTrace();
最终StackTraceElement元素=钢[ste.length-1-1];
方法=null;
试一试{
方法=获取方法(元素);
}捕获(ClassNotFoundException忽略){
//忽略类NotFound异常-只需确保该方法为null
方法=空;
}
布尔类型=真;
if(方法!=null){
//如果我们成功地获得了该方法,请保留该类型
//仅当它在抛出的异常列表中时
保存类型=假;
最终类异常[]=method.getExceptionTypes();
对于(类别cls:例外){
if(cls.isInstance){
保存类型=真;
打破
}
}
}
if(保存类型)
{
//它引发异常-保留类型
建造师;
异常newEx=null;
试一试{
构造函数=((构造函数)e.getClass().getConstructor());
newEx=constructor.newInstance(消息,e);
}捕获(异常忽略){
//忽略此异常我们更喜欢抛出原始
newEx=null;
}
if(newEx!=null)
扔掉纽克斯;
}
//如果我们到了这里,这意味着我们不想要,或者我们不能保留类型
//只需使用默认类型重新播放它
建造师;
异常newEx=null;
if(defaultException!=null){
试一试{
构造函数=(构造函数)defaultException.getConstructor();
newEx=constructor.newInstance(消息,e);
}捕获(异常忽略){
//忽略此异常我们更喜欢抛出原始
newEx=null;
}
if(newEx!=null)
扔掉纽克斯;
}
//如果我们到达这里,就无法构造默认异常
//让我们记录我们将要丢失的消息并重新播放
//最初的例外
log.warn(“此消息未作为异常的一部分传播:\”“+消息+”\”);
投掷e;
}

更新1: 我可以使用
RuntimeException
来避免抛出声明的需要,但是在这种情况下,我丢失了异常的类型,这是最重要的一点


如何解决这个问题?

我猜您正在进行实际工作的代码(即您没有修补异常的部分)是这样的

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);
     }
}
try {
...
} catch (Exception e) {
    throw new MyFunctionFailedException("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);
    }
}
第二种方法

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
s,那么第一种方法很好。一般来说,你不想这样做,最好让它们传播,因为你可能无法处理它们

第二种方法通常是最好的。这里,您明确指出可以处理哪些异常,并通过包装它们来处理它们。意外的
RuntimeException
s会像应该的那样向上传播,除非您有办法处理它们

仅作一般性评论:使用
StackTraceElement
s被认为不是一个好主意。您最终可能会从
Thread.currentThread().getStackTrace()
(尽管如果使用现代Oracle JVM,您很可能不会),并且调用方法的深度并不总是
length-2
,它可能是
length-1
,尤其是在旧版本的Oracle JVM中

您可以在中阅读有关此问题的更多信息。

要详细说明)一些人告诉您的内容,这是MyFunctionFailedException,当然应该将其命名为更合理的名称:

public class MyFunctionFailedException extends Exception {
    public MyFunctionFailedException(String message, Throwable cause) {
        super(message, cause);
    }
}
然后你的接球块变成了这样

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);
     }
}
try {
...
} catch (Exception e) {
    throw new MyFunctionFailedException("extra message", e);
}

如果您真的想重新引发较低级别的异常,那么应该使用多个catch块。请注意,并非所有类型的异常都必须有一个构造函数,让您添加原因。您真的应该考虑一下,为什么您的方法让一个未捕获的SQLException冒泡到调用堆栈中是有意义的。

不要这样做!!!您将丢失异常堆栈跟踪信息,这是异常中一些最重要的信息。相反,在旧异常是“原因”的情况下,抛出一个新异常(所有情况下都是一个“MyFunctionFailedException”类)。如果我将上一个异常作为参数传递给新异常,我想我不会这样做。只需抛出“MyFunctionFailedException”。这完全没有必要。如果你不能处理你所在的异常,就让它冒泡起来,直到你能处理它。如果您可以通过抛出一种完全不同的异常类型来处理它,那么就抛出另一种类型的异常