Reputation: 20862
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?
Upvotes: 1
Views: 1061
Reputation: 8589
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.
Upvotes: 3
Reputation: 19565
This null
value is used by printStackTrace
method e.g. when calling stackTraceString
method, lines 421..450 to identify when the output of the stack trace should be finished.
Upvotes: 1
Reputation: 100133
It isn't comparing cause
to null
, it's comparing cause
to this
. To avoid circles, if cause
is this
, it returns null.
Upvotes: 2