编程语言
首页 > 编程语言> > 用新消息重新抛出Java异常,如果异常类型在方法声明列表中,则保留该异常类型

用新消息重新抛出Java异常,如果异常类型在方法声明列表中,则保留该异常类型

作者:互联网

我正在尝试创建一个辅助方法,该方法将无需使用如下代码:

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列表.

这样,当我更改方法在抛出列表中传播的类型的列表时,无需更改主体.

以下是可以解决问题的功能.问题是因为它不知道它将抛出throw声明的异常类型必须说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来避免进行throws声明,但是在这种情况下,我丢失了异常的类型,这是最重要的一点之一.

我如何解决这个想法?

解决方法:

我猜您正在执行实际工作的代码(即,您不会修改异常的部分)看起来像这样.

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异常会传播,除非您有某种处理它们的方法,否则它们应该传播.

只是一个一般性的评论:玩StackTraceElements被认为不是一个好主意.您可能最终会从Thread.currentThread().getStackTrace()获得一个空数组(尽管如果使用现代Oracle JVM,您很可能不会这样做),并且调用方法的深度并不总是length-2,它可能长度应为1,尤其是在旧版本的Oracle JVM中.

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

标签:rethrow,exception,java
来源: https://codeday.me/bug/20191121/2052366.html