gpt4 book ai didi

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

转载 作者:搜寻专家 更新时间:2023-11-01 02:45:00 25 4
gpt4 key购买 nike

我正在尝试创建一个辅助方法,它将消除对这样的代码的需求:

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;
}

更新 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);
}
}

有两种更好的方法

第一种方法

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,它可能是 length-1,特别是在旧版本的 Oracle JVM 中。

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

关于java - 使用新消息重新抛出 java 异常,如果它在方法声明列表中,则保留异常类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24687989/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com