I had a reason to go into the source code for Throwable.getCause
today and was a little surprised to see this code in that method:
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
This is from Java 1.8, but it looks the same in the later versions I've looked at.
My question is: why not simply return cause
and be done with it?
It isn't comparing cause
to null
, it's comparing cause
to this
. To avoid circles, if cause
is this
, it returns null.
The code:
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
Says if the cause
is this
return null
otherwise return cause
(which may also be null
as it happens.
The story starts with this: private Throwable cause = this;
which is I believe the same in all versions >=1.4. That initialises the cause to being this object!
The intention is that Throwable
objects are immutable but it provides a method void initCause(Throwable cause)
that can only be called once to initialise the cause or cause initialised by a constructor.
As the documentation explains that allows the chaining of causes to sub-classes added before cause
was introduced that don't include it in one of their constructors.
So the class somehow wants to know if initCause
has been called and throws IllegalStateException
if it has (my comments):
public Throwable initCause(Throwable cause) {
if (cause == this) //Illogical! An exception can't be self caused!
throw new IllegalArgumentException();
if (this.cause != this)// false if cause has been initialised 'properly'.
throw new IllegalStateException();
this.cause = cause;
return this;
}
The class is using cause==this
to indicate cause
not set. It can't use cause==null
because null
is a valid value. So it uses the anomalous state of cause==this
because actively setting cause
to this
is the illogical state of a self-caused exception.
It works, sure. But is it really a good idea? I'm saying not. It conflates the states of "cause not set" and "cause has been set and is set to null
". A less contorted design just introduces a flag private boolean isCauseSet=false;
and setting it if initCause
is ever called or a constructor that sets it is called.
The convoluted code we see in Throwable
achieves nothing more than avoiding a boolean
. Saving a single boolean
field inside Throwable
really doesn't seem worth the bother. No useful application will ever have so many Throwable
objects in circulation for it to matter.
该null
值由printStackTrace
方法使用, 例如,在调用stackTraceString
方法时,第 421..450行标识堆栈跟踪的输出应何时完成。
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.