我遇到了一些我正在构建的java库中的地方,其中异常的原因被设置为异常本身。
是否有任何理由将异常引用为其原因?
修改
根据要求,这是一个具体的例子:
答案 0 :(得分:13)
我经常看到框架或库引发的异常,例如Hibernate或Spring引用本身作为原因(在过程中混淆了调试器GUI)。
我总是想知道为什么他们这样做,因为它似乎是个坏主意。今天,当我尝试将一个序列化为JSON时,它实际上引起了一个问题:bam,enless cycle。
所以我进一步调查了一下:
在Throwable
的源代码中(此处列出的所有源代码均来自JDK 1.7)我们有:
/**
* The throwable that caused this throwable to get thrown, or null if this
* throwable was not caused by another throwable, or if the causative
* throwable is unknown. If this field is equal to this throwable itself,
* it indicates that the cause of this throwable has not yet been
* initialized.
*
* @serial
* @since 1.4
*/
private Throwable cause = this;
现在我特意遇到了一个扩展RuntimeException
的异常类的问题,所以我从那里开始。 RuntimeException
的构造函数之一:
/** Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public RuntimeException(String message) {
super(message);
}
上面调用的Exception
的构造函数:
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public Exception(String message) {
super(message);
}
上面调用的Throwable
的构造函数:
/**
* Constructs a new throwable with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* <p>The {@link #fillInStackTrace()} method is called to initialize
* the stack trace data in the newly created throwable.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
fillInStackTrace
是一种原生方法,似乎无法修改原因字段。
您可以看到,除非随后调用initCause
方法,否则cause
字段永远不会更改this
的原始值。
结论:如果使用不带Exception
的构造函数创建新的cause
(或许多子类中的一个,并且不会覆盖此行为)参数,你不调用initCause
方法,异常的原因本身就是!
所以我想这应该是非常常见的事情。
答案 1 :(得分:10)
接受的答案具有误导性,其他答案不完整。所以......
虽然 是错误的设计以传递异常作为其自身的原因,但由于这个原因,它在Throwable实现中是不可能的。原因是在构造期间传入,或者传递给initCause()方法,并且如第二个答案所指出的,后者将导致IllegalArgumentException。
正如第三个答案所指出的,如果你没有提供原因,那么根据Throwable实施,原因将是 this 。
可能缺少的(给出原始问题)是Throwable的getCause()方法没有返回 this ,如果cause == this,则返回null。因此,虽然你的调试器显示 this 引用是因为它使用反射,但是当使用Throwable的公共接口时你不会看到它,所以它不会成为一个问题
答案 2 :(得分:3)
我来自Throwable
的来源:
public synchronized Throwable initCause(Throwable cause) {
...
if (cause == this)
throw new IllegalArgumentException("Self-causation not permitted");
...
}
我看不出设置原因是否可行。
答案 3 :(得分:2)
不,那只是糟糕的设计。如果异常是根本原因,则不需要定义原因。
具有原因的异常是不同异常包装的合法情况。例如,如果创建持久性存储,则可能需要抛出PersistenceExcpetion。那么如果它是一个文件存储,你可以让原因成为IOException。如果它是一个数据库,可能原因是SqlException。等