We know from the Java assertions document that AssertionError is a subclass of
Error since it is not really meant to be caught:
Q: Why is AssertionError a subclass of Error rather than RuntimeException?
A: This issue was controversial. The expert group discussed it at length, and came to the conclusion that Error was more appropriate to discourage programmers from attempting to recover from assertion failures. It is, in general, difficult or impossible to localize the source of an assertion failure. Such a failure indicates that the program is operating "outside of known space," and attempts to continue execution are likely to be harmful. Further, convention dictates that methods specify most runtime exceptions they may throw (with @throws doc comments). It makes little sense to include in a method's specification the circumstances under which it may generate an assertion failure. Such information may be regarded as an implementation detail, which can change from implementation to implementation and release to release.
Here we see Error contrasted with RunTimeException, which is what your
proposed IllegalStateException extends.
So the intent seems to be: this particular error should not happen. It is of
the same kind of severity as an AssertionError; glaring programming error, not
something to recover from. [1]
We might ask ourselves why AssertionError was chosen. Error
could have been used instead, right?
class Example {
private Example() {
throw new Error();
}
}
And the JavaDoc (for those curious about the likely intent, like we are)
directly documents what the purpose of Error is. Meanwhile AssertionError
just says that it is “Thrown to indicate that an assertion has failed.”
But it seems that we shouldn’t do this, according to this same book. [2] _Item 58 says that there is a strong convention
that errors are reserved for use by the JVM to indicate resource deficiencies, invariant failures, or other conditions that make it impossible to continue execution.
This talks about subclassing. So can we just use Error like above? It seems like it generally advices us to use something that inherits from RunTimeException. On the other hand (again) this is an invariant failure. So maybe it’s fine?
Well, you can’t go wrong with some RunTimeException instance since they are intended for programming mistakes too. But maybe AssertionError more clearly communicates that this is a very, very local invariant check.
Notes
- And using
AssertionError effectively achieves an unconditional—always
enabled no matter what the JVM arguments are—assertion statement, unlike
regular assert statements.
- Effective Java, Second Edition
assertstatements".AssertionError. I'd encourage anyone who is surprised by this pattern to read this page.