Pinpoint Code Problems Using Java’s Assertion Facility

eteran object-oriented language programmers who have used non-Java languages are accustomed to having assertions in their development toolbox. An assertion is an expression that evaluates to either true or false. Assertion failures are treated as unchecked exceptions. Programmers use such failures as an indication that the program deviated from the intended functionality. Assertions can help improve the quality of your code by letting you find and fix problems before you ship it to production.

In the past, Java had no assertions feature to help programmers test assumptions about the state of their applications during execution.   Fortunately, Sun included assertions as part of the Java 2 Standard Edition (J2SE) 1.4 as a result of Java Specification Request (JSR) 41?proof that the JSR process can work. This article introduces you to the assertions feature. You’ll see when and when not to use assertions in your code.

Designing By Contract
The “Design By Contract” theory states that an application creator and application consumer have a contract that determines how the application should function. Assertions follow the theme of designing by contract, because you can use them to verify that contractual line items have been met. Assertions test the correctness of assumptions made in your program.

Exceptions versus Assertions
You might wonder why assertions are even needed when exceptions are available. You use Java exceptions to handle unusual conditions during execution. On the other hand, you use assertions to affirm or assert conditions which you assume are true. In other words, assertions test to see if a condition occurs that should never happen. Assertions are meant to be development aids.  As it’s highly likely that assertions won’t be enabled during production runs (they’re off by default), your program should not rely on assertions to execute correctly at runtime.

For example, you might use an assertion in your code to show that the execution path never reaches the default condition in a switch statement (if that is what you intended). Also, you might assert that only one argument is passed in as a command line argument (again, if that is what you intended).

Using Assertions
When an assertion fails, the execution engine throws an AssertionError onto the stack trace. Note that you’re dealing with an error, not an exception. The currently executing function aborts when the error is thrown,

Assertions in Java take two forms:

   assert booleanExpression;   assert booleanExpression : aNonVoidExpression;

The first form takes a Boolean expression as an argument. The simple program shown below uses this flavor of the assertion to check that i>0.

   public class AssertSimple   {         public static void main (String args[])      {         System.out.println("Simple Assert Example");         int i = 3;         assert i>0;         System.out.println("Value of i: " + i);         i = -4;         assert i>0;         System.out.println("Value of i: " + i);         System.out.println("End of program");      }   }

Figure 1 shows the output from executing the preceding code. Note that the program was run twice: the first time without enabling assertions (the default), and the second time with assertions enabled.

 
Figure 1: Execution of AssertSimple: The program was run twice: once with assertions disabled and then again with assertions enabled

The second flavor of the assertion assert booleanExpression : aNonVoidExpression is similar to the first, except that when the Boolean expression evaluates to false, the runtime creates an AssertionError object using the data returned from evaluating the expression following the colon. The returned data type is converted to a string and dumped onto the stack trace. The expression must return a value?in other words, it must be a non-void expression.

The sample file AssertSecondFormSimple.java defines the class AssertSecondFormSimple shown below, which exemplifies the second assertion form.

   public class AssertSecondFormSimple   {         public static void main(String args[])      {         System.out.println(            "Another Simple Assert Example");         int i = 3;         assert i > 0: "i Must be greater than " +            "zero.".toUpperCase();         System.out.println("Value of i: " + i);         i = -4;         assert i > 0: "i Must be greater than " +             "zero.".toUpperCase();         System.out.println("Value of i: " + i);         System.out.println("End of program");      }   }

The AssertSecondFormSimple class code could just as easily have used an uppercased clause after the assert, as in the following line.

   assert i>0 : "I MUST BE GREATER THAN ZERO.";
 
Figure 2. Execution of AssertSecondFormSimple. The error message resulting from evaluating the expression after the colon in the second assertion form appears when the assertion fails.

However, I intentionally used the toUpperCase method to show that the expression is evaluated (not just a string copy operation) when the assertion fails. In this case, the String’s toUpperCase() method returns a string object which is in turn passed to the AssertError class constructor. Figure 2 shows how the object ends up being presented to the developer when the program balks during execution.

Toggling Assertions During Compilation and Execution
As exemplified in Figure 1, the assertions feature is disabled by default at run time. This was done to solve binary compatibility issues with previous versions of Java that do not support assertions. Consequently, to use assertions, you first need to compile programs with the ?source flag set to a value of 1.4 as shown below:

   javac --source 1.4 AssertExample.java

In addition to enabling assertions during compile time, you also need run the program with assertion checking enabled, which you do via the ?enableassertions option (or ?ea for short).

   java --enableassertions AssertionExample 

Fortunately, enabling assertions isn’t an all-or-nothing proposition. You don’t have to enable assertions for your entire application; you can specify the granularity of your assertions. For example, you can enable assertions for a single package by using the ?ea option followed by a colon and name of the package. Following the package name by an ellipsis (?) enables assertion checking for the package and for its subpackages, for example:

   java --ea:com.example... AssertionExample

You can exclude a class from assertion checking using the?disableassertions or?da option followed by a colon and the name of the class to exclude.

   java --ea:com.example… -da:com.example.ClassToExclude AssertionExample

The preceding code disables assertions for the com.example.ClassToExclude class while enabling it for all the other classes in the com.example package.

Enabling Assertions Programmatically
While you will most likely want to enable assertions via the command line, the Java 1.4 assertion facility also allows you to enable and disable assertion checking programmatically. For example, the class AssertProgrammatically (shown below) uses the class Auxiliary (also shown below). Note that the AssertProgrammatically class enables assertions for the Auxiliary class when it loads the class.

   public class AssertProgrammatically   {         public static void main(String args[])      {         System.out.println(            "Example of enabling assertions " +            "programmatically.");         ClassLoader classLoader =            AssertProgrammatically               .class               .getClassLoader();         classLoader.setClassAssertionStatus(            "Auxiliary",            true);         int i = 3;         System.out.println("Sending: " + i);         Auxiliary auxiliaryI = new Auxiliary(i);         System.out.println(            "i is: " + auxiliaryI.whatAmI());         i = -3;         System.out.println("Sending: " + i);         Auxiliary auxiliaryJ = new Auxiliary(i);         System.out.println(            "i is: " + auxiliaryJ.whatAmI());         }   }   public class Auxiliary   {      private int myInt;      Auxiliary(int incoming)      {         myInt = incoming;      }      public int whatAmI()      {         assert myInt>0 : "i must be positive";         return myInt;      }   }

As the preceding code shows, to programmatically enable or disable assertions, you first obtain the ClassLoader of the class, and then enable or disable assertions using a line such as:

   classLoader.setClassAssertionStatus("Auxiliary",true);
 
Figure 3. Executing the AssertProgrammatically Class: From the output shown in this figure, you can see that the assertion checking doesn’t occur via the command line; instead, assertion checking was turned on via a method call.

You provide the name of the class for which you want to control assertions as the first method argument (the class was named “Auxiliary”). The second argument value of true controls the assertion state (true or false). The ClassLoader object also provides methods for enabling and disabling assertions at a package level, using the setPackageAssertionStatus() method. Figure 3 shows the output from executing the AssertProgrammatically class. Unlike Figure 1, note that the assertion checking did not occur via the command line (using the?enableassertions option); instead, the setClassAssertion method call in the AssertProgrammatically class turned assertion checking on.

Author’s Note: Setting assertion checking for a package or class affects only classes loaded after the enablement change.

Don’t Rely On Assertions to Ensure Program Flow
As you’ve seen, the person running your application can turn assertions off (again, they are turned off by default). Consequently, you don’t want to place assertions in your code that must be evaluated for the application to function correctly.

To reiterate, don’t place actions (methods, operations, etc.) in your assert statements that will affect subsequent program execution, because there’s no guarantee that the assertions will be evaluated. You should typically use assertions only in debugging modes, not in production deployment.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: