I'm not sure if this question was asked before, but I've been always wondering why every instance of Throwable
, unless the fillInStackTrace
method is overridden/suppressed, fills its stacktrace in the constructor while being instantiated by default.
Consider the following code:
final class ThrowableTest {
private ThrowableTest() {}
public static void main(final String... args) throws Throwable { throw go(); }
private static Throwable go() { return goDeep(); }
private static Throwable goDeep() { return goDeeper(); }
private static Throwable goDeeper() { return goEvenDeeper(); }
private static Throwable goEvenDeeper() { return new Throwable(); }
}
Executing the code above would produce the following output:
Exception in thread "main" java.lang.Throwable
at ThrowableTest.goEvenDeeper(scratch.java:9)
at ThrowableTest.goDeeper(scratch.java:8)
at ThrowableTest.goDeep(scratch.java:7)
at ThrowableTest.go(scratch.java:6)
at ThrowableTest.main(scratch.java:5)
I would find the following hypothetical stacktrace more intuitive (assuming the fillInStackTrace
method is not overridden and the Throwable
constructor does not invoke it in such a "special" JRE):
Exception in thread "main" java.lang.Throwable
at ThrowableTest.main(scratch.java:5)
I'm wondering: why is the stacktrace filled in the constructor (in its simplest scenario) rather than at the exception throwing site using throw
?
If I'm not mistaken, there are some scenarios when fillInStackTrace
can be used to configure the exception to be (re)thrown, and the throw
as described would re-write the stacktrace to the current one.
But if so, the throw
statement (and the athrow
instruction) might check if the stacktrace of the exception is set to null
before filling it up on its own (assuming that throwing something other than an instance of Throwable
is undefined as it's described in the specification).
What was the design choice behind it?