Java Exception Handling

Java Exception Handling – AssertionError

Making our way through our detailed Java Exception Handling series we arrive at the AssertionError that we’ll be going over today. As with many other languages, the AssertionError in Java is thrown when an assert statement fails (i.e. the result is false).

Within today’s article we’ll explore the detailed of the AssertionError by first looking at where it sits in the larger Java Exception Hierarchy. We’ll also examine some fully functional Java code that will illustrate one example of a best practice for using assertions in Java applications, and how such code should lead to an unexpected AssertionError if something goes wrong. Let’s take a look!

The Technical Rundown

All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

This code sample also uses the Logging utility class, the source of which can be found here on GitHub.

When Should You Use It?

To understand how AssertionErrors should be thrown within Java code we should first briefly review how assertions and the assert keyword are intended for use. The purpose of assertion is to test assumptions about the application’s logic. These assumptions might include pre-conditions, post-conditions, invariants, and the like. As with most other programming assertion features, the Java assert keyword expects a boolean expression that the code assumes will be/should be true. This is because, the moment an assert expression is false the Java Virtual Machine (JVM) will throw an AssertionError, which should typically halt execution of the application.

Before we go any further it’s worth noting that, by default, assertions are disabled for most JVMs. Thus, executing code with a failing assertion will completely ignore the failure and will not throw an AssertionError. This is typically not desired, since the purpose of using assert expressions at all is to properly test the assumptions throughout your code. To enable assertion checking you’ll need to add a command-line switch: -enableassertions or -ea. For example, if you’re executing code via the java command line tool, you’d need to issue something like the following command: $ java -ea <class>. For most IDEs such as IntelliJ IDEA or Eclipse, you should look in the run configuration for JVM/VM command-line options, in which you can add the -ea flag.

There is a bit of discussion and debate in the Java development community about the exact and proper use of assertions, but the general consensus is that the appearance of an AssertionError should indicate a fundamentally broken application/code snippet. Consequently, assert statements should be used as a form of sanity checks as final “no-turning-back” statements that should never be reachable by proper code. In other words, it is common to use an assert statement that always produces a false value, yet in a location where execution of the assert statement should not be possible.

To illustrate this particular usage of assertions we’ve created a modified version of our Book class called BookWithStringPublicationType:

As the name indicates we’re using a simple String value to store the PublicationType field of each Book. We then perform a simple sanity check prior to setting the value within the setPublicationType(String type) method:

As you can see we perform a simple switch case test on the passed String type parameter. If it is one of the two valid values we set the value and explicitly break from the switch statement. However, any other value will reach the default case, which contains an always-false assert false expression, with an additional argument passed to it that will be used for the error message of the subsequent AssertionError instance. In this case, the goal of the code is to be completely certain that a publicationType field can never be set to something that is invalid. Attempting to do so will produce a failure via an AssertionError, which will require alteration by a developer to fix the bug.

To illustrate how this works in practice we have the testEnumPublicationType method:

We start by creating a valid BookWithStringPublicationType instance with a publicationType property of "PAPERBACK". We then try to change to publicationType to "INVALID" before outputting the resulting object. To give us something to show when a failure occurs we’re explicitly catching any AssertionErrors that are thrown here, but in normal code we would not want to catch such errors, allowing them to instead crash the application.

Executing the testStringPublicationType() method produces the following output:

Everything works just as expected. Our original BookWithStringPublicationType instance as a "PAPERBACK" is instantiating just fine, but attempting to change it to "INVALID" throws an AssertionError, since execution within the BookWithStringPublicationType.setPublicationType(String type) method reached the final, default switch case and executing the assert false statement.

While this example illustrates one common way to use assertions in Java, we can also modify how the BookWithStringPublicationType class handles the publicationType to ensure it doesn’t rely on an assertion statement, while also ensuring that code cannot try to set invalid values for the field. We do this for the plain Book class by using the PublicationType enum:

Since only the publicationType field and its related getter/setter methods were modified to use the PublicationType enum we’ll only look at these changes in code:

As with enumerations in other languages, using one in Java allows us to maintain a collection of valid values for a particular data type. To illustrate this the testEnumPublicationType() method seen below creates an intial Book instance with the PublicationType.PAPERBACK value for publicationType, then attempts to invoke the book.setPublicationType(PublicationType.INVALID) method call to change it:

As it happens, since the JVM can identify the enumeration that is used here during compilation time, we can’t even execute this code — the compiler detects that PublicationType.INVALID is, well, not a valid symbol within the PublicationType enum, so it halts compilation and delivers an error message. This implementation serves the same purpose of the assert technique seen above, except it captures an issue at compilation/development time, rather than during runtime.

The Airbrake-Java library provides real-time error monitoring and automatic exception reporting for all your Java-based projects. Tight integration with Airbrake’s state of the art web dashboard ensures that Airbrake-Java gives you round-the-clock status updates on your application’s health and error rates. Airbrake-Java easily integrates with all the latest Java frameworks and platforms like Spring, Maven, log4j, Struts, Kotlin, Grails, Groovy, and many more. Plus, Airbrake-Java allows you to easily customize exception parameters and gives you full, configurable filter capabilities so you only gather the errors that matter most.

Check out all the amazing features Airbrake-Java has to offer and see for yourself why so many of the world’s best engineering teams are using Airbrake to revolutionize their exception handling practices!