The Java assert statement can be used for conditionally checking program invariants. Assertions are enabled or disabled with the desired level of granularity by Java Virtual Machine flags, as described in the linked guide. So far, so similar to assertion facilities in other languages.
However, whereas .NET Debug/Trace.Assert shows and logs a message by default, and C++ assert unconditionally terminates the program, Java assertion failures throw an AssertionError.
You might think that would be an Exception. Even the Oracle guide linked at the top sloppily speaks of “uncaught exceptions” from assertion failures. Well, this is wrong.
Quick Demonstration
First, a small sample program to show how to catch (or not) Java assertion failures. Compile the following program and run it with java -ea
to enable assertions.
public class ThrowableTest {
public static void main(String[] args) {
int x = 0;
try {
assert(x > 0);
}
catch (Exception e) {
System.out.println("Exception caught");
}
}
}
This does not catch the assertion failure but rather terminates the program with an error message and stack trace. Lamentably the message begins with “Exception in thread “main”…” so as to maximize programmer confusion. The error was a Throwable
, not an Exception
. Here is the corrected version that will catch the assertion failure:
public class ThrowableTest {
public static void main(String[] args) {
int x = 0;
try {
assert(x > 0);
}
catch (Throwable e) {
System.out.println("Throwable caught");
}
}
}
Throwable vs. Exception
Aside from its notorious checked exceptions, another peculiarity of Java’s runtime error API as compared to .NET is that Exception
is not the base class for all errors – Throwable is. Exception
is one of two subclasses of Throwable
. The other is Error.
As per its documentation, Error
represents “serious problems” and “abnormal conditions” that a “reasonable application should not try to catch.” For the most part that is undoubtedly true (e.g. VirtualMachineError
) but there is one glaring exception (sorry): AssertionError.
What are assertion failures doing in this company? Should they not result in some kind of RuntimeException, the base class for all other types used to check method arguments and object states? Under The AssertionError Class, Oracle explains that “this issue was controversial” but eventually decided in favor of a C++-style interpretation where assertion failures should generally just terminate the program and not be recovered from.
Background Threads
You may or may not find this reasoning convincing, but there is one important case that the Java expert team seems to have ignored: background threads. When an assertion failure occurs in a background thread, it is by default neither reported nor aborts the program. It just quietly terminates the thread (or the current operation in a thread pool), leaving a programmer who catches only the Exception
class – as advised by Oracle! – scratching his head and wondering whatever happened.
This was in fact how I stumbled on the curious nature of AssertionError
. Eventually I figured out that the top-level caller on my background thread had to use a try/catch
block with Throwable
rather than Exception
. Then the assertion errors were correctly caught and reported. Presumably for the same reason, Java’s own Thread.UncaughtExceptionHandler facility also reports any Throwable
, and not just Exception
as one might infer from the class name.
Thank you for this article. Excellent explanation.
Thanks, glad you found it helpful.
Note that you can also }catch (AssertionError e){ doYourStuff }