Thursday, August 11, 2011

Java : Assertions

Working with the Assertion Mechanism


Suppose you assume that a number passed into a method (say, methodA()) will never be negative. While testing and debugging, you want to validate your assumption, but you don't want to have to strip out print statements, runtime exception handlers, or if/else tests when you're done with development. But leaving any of those in is, at the least, a performance hit. Assertions to the rescue!


Check out the following code:

private void methodA(int num) {
if (num >= 0) {
useNum(num + x);
} else
// num must be < 0
// This code should never be reached!
System.out.println("oops! num is a negative number! " + num);
}
}


Because you're so certain of your assumption, you don't want to take the time (or program performance hit) to write exception-handling code. And at runtime, you don't want the if/else either because if you do reach the else condition, it means your earlier logic (whatever was running prior to this method being called) is flawed. Assertions let you test your assumptions during development, but the assertion code basically evaporates when the program is deployed, leaving behind no overhead or debugging code to track down and remove. Let's rewrite methodA() to validate that the argument was not negative:



private void methodA(int num) {


assert (num>=0); // throws an AssertionError
// if this test isn't true
useNum(num + x);
}




Not only do assertions let your code stay cleaner and tighter, but because assertions are inactive unless specifically "turned on" (enabled), the code will run as though it were written like this:



private void methodA(int num) {
useNum(num + x); // we've tested this;
// we now know we're good here
}



Assertions work quite simply. You always assert that something is true. If it is, no problem. Code keeps running. But if your assertion turns out to be wrong (false), then a stop-the-world AssertionError is thrown (that you should never, ever handle!) right then and there, so you can fix whatever logic flaw led to the problem. Assertions come in two flavors: really simple and simple, as follows:


Really simple:


private void doStuff() {
assert (y > x);
// more code assuming y is greater than x
}

Simple:

private void doStuff() {
assert (y > x): "y is " + y + " x is " + x;
// more code assuming y is greater than x
}

The difference between the two is that the simple version adds a second expression, separated from the first (boolean expression) by a colon, this expression's string value is added to the stack trace. Both versions throw an immediate AssertionError, but the simple version gives you a little more debugging help while the really simple version simply tells you only that your assumption was false.


Running with Assertions

Here's where it gets cool. Once you've written your assertion-aware code (in other words, code that uses assert as a keyword, to actually perform assertions at runtime), you can choose to enable or disable your assertions at runtime! Remember, assertions are disabled by default.

Enabling Assertions at Runtime

You enable assertions at runtime with

java -ea com.TestAssertions
or
java -enableassertions com.geeksanonymous.TestClass

The preceding command-line switches tell the JVM to run with assertions enabled.




Disabling Assertions at Runtime

You must also know the command-line switches for disabling assertions,

java -da com.TestAssertions
or
java -disableassertions com.TestAssertions

Because assertions are disabled by default, using the disable switches might seem unnecessary. Indeed, using the switches the way we do in the preceding example just gives you the default behavior (in other words, you get the same result regardless of whether you use the disabling switches). But…you can also selectively enable and disable assertions in such a way that they're enabled for some classes and/or packages, and disabled for others, while a particular program is running.



Example


/* TestAssertions.java */
public class  TestAssertions
{
public static void acceptsEvenNumber(int num)
{
if(num %2 != 0)
{
System.out.println("Please enable Assertion");
}
System.out.println("It is Even number : "+num);
}


public static void main(String[] args) 
{
//int num = 10;
int num = 11;
assert(num %2 == 0);// throws an AssertionError
// if this test isn't true


acceptsEvenNumber(num);
}
}


Output



C:\Java>java TestAssertions
Please enable Assertion
It is Even number : 11


C:\Java>java -ea TestAssertions
Exception in thread "main" java.lang.AssertionError
        at TestAssertions.main(TestAssertions.java:18)