HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Java's new assertion mechanism, a welcome addition to the language now available in version 1.4, allows programmers to increase the robustness of their code by sprinkling it liberally with assert statements. The new assertion feature is easy to use, but any language feature, no matter how simple, can be used well or poorly. Here I'll explain how to use Java's assertion facility, and how not to misuse it.

Using assertions couldn't be easier. Anywhere you can put a statement in Java, you can now write

assert boolExpr;

where boolExpr is any Boolean expression. If the expression is true, execution continues as if nothing happened. If it's false, an exception is thrown. You can disable assertions at runtime if you wish, effectively removing them from your code.

Why Use Assertions?
Assertions are a cheap and easy way of building confidence in your code. When you write an assertion, you're enabling the machine to check your beliefs about your program. You write the assertion thinking that it's true, but as you well know, not all of your beliefs about your code are true - if they were, you wouldn't have any bugs. Assertions can help uncover bugs early on.

For example, here's a bit of code that makes a choice based on the remainder of dividing n by 3:

if (n % 3 == 0) {
...
} else if (n % 3 == 1) {
...
} else {
assert n % 3 == 2;
...
}

Now obviously, n % 3 == 2 in the final else of this statement, since the remainder when you divide a number by 3 is either 0, 1, or 2. So there's no need to do an explicit test. But these new assert statements are easy to write and you can always disable them, so you add one just for kicks. That will turn out to have been a wise choice when the code is run with a negative value of n. The Java % operator, like the mod operator of most programming languages, gives negative results when its left operand is negative: -5 % 3 is -2, not 2. The assertion will fail, the program will stop, and you can easily correct both the code and your false belief about how % works.

Here's another example from some recent code of my own:

TransactionEntry te = (TransactionEntry)
assoc.getEntry(key);
if (te == null) {
te = new TransactionEntry(key, dur);
assoc.put(key, te);
} else {
assert te.getState() == te.REMOVED;
te.recreate(dur, session);
}

What this code is about doesn't matter. As you can tell just from the control flow, I firmly believe that if assoc.getEntry(key) returns a nonnull TransactionEntry, then that TransactionEntry must be in the REMOVED state. This is a desired property of my system and is enforced elsewhere (or so I believe), but is far from obvious in this piece of code. The assertion both documents my belief and confirms it at runtime, making me a little more confident that my system is correct.

The Details
Having seen why assertions are a good idea, let's look at Java's assertion facility in more detail.

The Java language has a new statement, the assert statement, which takes one of two forms. The simpler form is the one introduced earlier:

assert boolExpr;

If the expression evaluates to true, nothing happens, but if it evaluates to false, an AssertionError is thrown. The new class AssertionError is a subclass of Error.

If you want to include an additional information string with the AssertionError, provide it after the Boolean expression:

assert a.length != b.length:
"array lengths not equal";

The second expression can in fact be of any type, and will be converted to a string in the usual way.

Assertions are disabled by default in Sun's JVM. You can selectively enable them by class loader, package, or class by calling some new methods of the ClassLoader class, or more commonly via command-line arguments when you invoke the Java Virtual Machine. The command-line arguments aren't part of the spec, but in Sun's implementation you would use the -ea and -da options to enable and disable assertions, respectively. For example, to enable all assertions except for those in the com.astrel.util package, you would write

java -ea -da:com.astrel.util MyApp

If you then wanted to reenable assertions for the Heap class within com.astrel.util, you could write

java -ea -da:com.astrel.util \
-ea:com.astrel.util.Heap MyApp

Although the ClassLoader methods can be invoked at any time, they'll only affect classes loaded after the call. In other words, the status of a class's assertions - enabled or disabled - is determined once and for all when the class is loaded.

No method will tell you whether the current class has assertions enabled, but you can easily determine this with the following code:

boolean enabled = false;
assert enabled = true;

Here the assert statement is used only for its side effect. If assertions are enabled, the Boolean will be set to true; otherwise it will remain false.

That's all there is to using assertions. Now some guidelines on using them well.

Tips on Using Assertions

  • Disable assertions for deployment.
    Some people say that assertions should never be disabled. Disabling assertions for deployment, as the saying goes, is like throwing the lifeboats overboard just before the ship leaves port. I agree in principle, but disagree on a technicality. The main benefit of Java assertions is the ability to disable them. If you don't plan on disabling them, you shouldn't be using them. I do believe, and fervently, that your code should perform as much internal checking as is feasible, and that these checks should not be removed. But such checks shouldn't be assertions, in the narrow technical sense of being written with Java's assert statement.

    For the remainder of this article, I'll use the word "check" to mean any self-validating bit of code that shouldn't be disabled.

  • Make it possible to reenable assertions at any time.
    Although your deployed program will run with assertions disabled, it should be possible even for the end user to reenable them when necessary by restarting the application with an appropriate flag. The assertion facility is designed so that assertions can remain in the deployed class file and yet still have no impact on running time - disabled assertions can be removed by the class loader or JIT compiler. (That's why you can't change the assertion status of a class dynamically.) Thus your application need only provide access to the appropriate JVM command-line option in order to enable assertions in the field. By providing this ability, you give yourself another tool for catching bugs in the wild, under conditions that you may not be able to duplicate on your own.
  • Don't use side effects in assertions.
    Since assertions will be disabled in deployed code, they should not change the state of the system - otherwise, the system would behave differently when deployed. (It may behave differently anyway, because disabling an assertion may affect the timing of multithreaded programs, but hopefully your design is robust to withstand such minor timing changes.) There are a couple of techniques that violate this rule: one, which detects whether assertions are enabled, we saw above; another I'll describe later when I discuss postconditions.
  • Assertions should be expensive.
    In other words, either the Boolean expression should take a long time to execute, or the cumulative time of executing the assertion in typical runs of your program should be large. If the assertion doesn't slow you down, why bother disabling it? Or to put it another way: write cheap tests as checks, not assertions. Those checks will help you catch bugs in the deployed system without impacting performance. You can't beat that.

    Of course, if assertions as a whole are too expensive, your program will run too slowly to test, and you'll test less frequently. In this way, assertions can actually decrease the robustness of your code. Here is where the ability to selectively disable assertions comes in handy. When a class or package is first coming up to speed, enable assertions in it to gain confidence in its correctness. When the program becomes too slow to test frequently, disable assertions in the older, well-tested parts of the system.

  • If you can recover from it, don't assert it.
    Assertions throw an error instead of an exception because their purpose is to crash your program. (The whole reason behind having separate Error and Exception classes is that Errors indicate faults from which you shouldn't try to recover.) So catching an AssertionError and trying to continue is a sure sign that you should be doing a check, not an assertion. It's reasonable to catch an AssertionError, but only to log or otherwise process it before your program exits. In other words, any such catch should end by rethrowing the error.
  • Don't mention assertions in documentation.
    An assertion is an implementation choice, like the name of a local variable; whether it's enabled, disabled, or exists at all should in no way affect the contract of a method. (But feel free to boast to your co-workers that you've made your code more robust by "asserting the heck out of it.")
  • Don't use assertions for argument checking.
    If your method's contract claims it will check for null arguments, then you should do just that - check, not assert. There are two problems with using an assertion. First, the assertion will be disabled in production runs of the program, so your method won't be up to spec. (That really is like throwing the lifeboats overboard.) Second, the assert statement throws the wrong kind of thing, an AssertionError. If an argument is null, you should be throwing a NullPointerException; if it's generally invalid, an IllegalArgumentException; and so on.

    If you're tempted to use assert anyway because you can write the brief

    assert arg != null;

    in place of the verbose

    if (arg == null)
    throw new NullPointerException();

    then how about writing a helper method? In fact, let's postulate a class full of them:

    Check.isNotNull(arg);

    will throw a NullPointerException, while

    Check.legalArg(arg.length > 0);

    would throw IllegalArgumentException. (Two-argument versions of these methods would take message strings to be included in the exception, just like the assert statement.) Concerned about the overhead of an extra method call? Don't be: these Check methods are static and small, which means that a good JIT compiler could easily inline them. (On my system, running JDK 1.3 with the HotSpot Client VM, there's no measurable time difference between calling a Check method and writing the same code inline.)

    All that being said, if checking an argument is expensive, consider using an assertion instead (and don't claim in the documentation that the argument is checked). Another time to consider an assertion over a check is when you control all calls to the method - for example, when the method is private. Since you can guarantee that all calls to the method are correct, you don't need a check, but an assertion couldn't hurt.

  • Use assertions for preconditions and postconditions.
    A precondition is something your method assumes to be true on entry. Restrictions on arguments are one kind of precondition, but not the only kind: your method might require that other parts of the system be in a certain state before it can validly proceed. Once you've identified a precondition and decided that you don't want to turn it into a check (presumably because it's too expensive), asserting it is a good idea.

    One kind of precondition is a class invariant - a statement about a class or its instances that should be true before and after each of the class's methods. For example, say you are writing a Heap class that implements a binary heap - a binary tree with the property that the value stored in each node is no less that the values stored in its children. This property is a class invariant. (Heaps are useful for building priority queues, among other things.)

    At the beginning of the method that adds a new item to the heap, you expect that the heap property is true. Indeed, the standard algorithm for adding an item to a binary heap won't work correctly unless it's true. So the heap property is a precondition of the add method. It would be wise to write an isValid method in the Heap class that checks the property, and place

    assert isValid();

    at the start of the add method.

    Similarly, a postcondition is something that should hold when the method is finished. One seldom checks postconditions, but asserting them makes sense. Class invariants are postconditions as well as preconditions, so it would be a good idea to assert the heap property at the end of each Heap method as well as at the beginning.

    If a method changes its parameters, chances are the method's postcondition will need access to the original parameter values. You can write the postcondition by storing copies of the original values before they're modified. For example, the postcondition of any sort routine is that the input array be in sorted order, and that it consist of the same elements as before the sort. While the first condition does not require the original array, the second one does.

    Copying parameters is expensive, so it should happen only when assertions are enabled. You can achieve that by using a side effect in an assertion:

    void sort(int[] a) {,
    int[] old_a = null;
    assert (old_a = (int[]) a.clone()) != null;
    // do the sort
    assert inSortedOrder(a) &&
    hasSameElements(a, old_a);
    }

    The inequality check in the first assertion is there only to produce a Boolean that is always true; the assertion's real job is to clone the array. If assertions are disabled, no copying will occur.

  • Use assertions for those hard-to-reach places in the middle.
    Assertions are great for places in the middle of methods when part of the job is done. An assertion in the middle of a method will catch problems sooner than one at the end, and may have less work to do as well. For instance, part of the process of adding a new item to a heap involves repeatedly comparing a node's value in the tree with those of its two children, and then possibly swapping the node with its larger child. Placing an assertion after this piece of code, to the effect that the heap property still holds for the parent node and its children is cheaper than testing the whole heap for validity at the end, will catch any problems in the code right where they happen, and acts as useful documentation too.
  • Don't use assertions to flag "unreachable" code.
    We often write switch statements whose default case we know cannot happen, and sometimes the compiler forces us to catch exceptions that we know cannot be thrown. Some suggest using assertions in these and other allegedly impossible-to-reach places, but I disagree. Checks are preferred: they will not be disabled and, by assumption, will not affect performance, since they will never run. Simply writing a throw statement would be adequate (although exactly which class of exception should be thrown is not clear), or we could postulate a Check.impossible() method that throws the exception and write:

    switch (x) {
    case 1: ...; break;
    case 2: ...; break;
    default: Check.impossible();
    }

  • Use assertions as executable documentation.
    Occasionally you may get the impulse to write a brief comment in the middle of your code to the effect that, for example:

    // at this point, x is greater than y

    Instead of commenting it, assert it:

    assert x > y;

    The assertion is as valuable as the comment to the human reader, and it's machine-checkable as well.

  • Treat assertions as tests.
    Assertions are the first line of testing - they are subunit tests. Once you realize this, many of the above guidelines fall out almost automatically. You disable assertions in production for the same reasons that you don't ship your test code. Asserting the postcondition of a public method is much like writing a unit test for that method. And the importance of midmethod assertions is clear: they're located in places where you can't otherwise test.
  • Assert early and often.
    Write assertions as you write your code - or even before, if you're a devotee of Extreme Programming. That is when the logic's details are freshest in your mind. And feel free to write plenty of them, since you can always disable them.
Conclusion
Although assertions are a small addition to Java - just one new statement - they can have an impact out of proportion to their size. Like a well-written test, a well-placed assertion will catch bugs early in the development process, when it's cheapest to fix them. They serve as helpful documentation to readers of your code. And since assertions can be disabled at runtime, your program's performance need not suffer. So don't hesitate to use them. Assertions are sure to improve the quality of your code.

Author Bio
Jonathan Amsterdam is a senior consulting engineer at DataSynapse, Inc. Formerly, he was president and founder of Astrel, Inc., a Java training and consulting firm. He's also an adjunct professor of computer science at New York University. [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.