简体   繁体   中英

Why does Throwable.getCause check to see if 'cause' is 'this' before returning `null` instead of just returning the cause directly?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM