Programming with Assertions in Java Part 2
Sep 24, 2018 • 6 Minute Read
Introduction
In the previous guide, Programming with Assertions in Java Part 1, you learned how to use assertions.
In this guide, I'll discuss some best practices for using assertions and where not to use them.
Best Practices for Using Assertions
In some cases, assertions are the way to go!
Using Assertions Instead of Comments
For documentation purposes, try something like the following:
void printTotals() {
// ...
String amountStr = amountToString(amount);
// amountStr should not be null
System.out.println("Amount: " + amountStr);
// ...
}
It's even better to enforce this check when testing or running a program. Assertions can highlight potential bugs:
void printTotals() {
// ...
String amountStr = amountToString(amount);
// amountStr should not be null
assert amountStr != null;
System.out.println("Amount: " + amountStr);
// ...
}
Never again rely on using comments and a debugger. Use assertions instead!
Using Assertions to Validate Unreachable Code
This can be especially helpful when you don't want the execution flow to reach a certain point of your program. Like in the following example, you can place an assertion in the default clause of a switch statement to add extra protection:
switch(type) {
case 1:
//...
break;
case 2:
//...
break;
case 3:
//...
break;
default:
assert false : "Default case reached";
}
However, you must take into account that truly unreachable code can generate a compile-time error:
if(interest < 0) {
return 0;
} else {
return interest;
}
assert false : "A valid interest should be returned"; // Compile time error
Using Assertions to Verify the Result of an Operation
Don't think of assertions of something only used to check input values or enforce control flow. Remember, you can place an assertion statement anywhere in your program.
An assert statement checks a boolean expression. However, you can use methods to check the results of an operation using complex criteria:
String defaultPassw = generateDefaultPassword();
assert isValidPassword(defaultPassw);
Many beginners choose to use print statements here. Assertions go a step further by actually evaluating code and flagging it if it does not meet expectations.
Don't Be Afraid to Fill Your Code with Assertion Methods
The more you check your assumptions, the more confident you'll be that those assumptions are guaranteed to be met when executing your code.
Even if something may seem obviously true to you, remember that bugs can appear in the least expected places. Sometimes it's difficult to see all the possible ways a condition can fail.
Yes, this may bloat your code but the advantages of fixing bugs earlier and less debugging time (the stack trace of an AssertionError) can help you pinpoint the source of the error.
And if you're concerned that all your assert statements will harm your performance, don't be.
First of all, most of the time, assert operations are simple comparisons that get enabled in development and disabled in production. Remember, they're internal checks to make sure a developer's assumptions are correct. They should not change the behavior of the application.
However, you can make use of a Java compiler optimization by placing all the assert statements inside an if-block that tests for a static final false value:
static final boolean assertsEnabled = false;
// ...
if (assertsEnabled) {
assert expr1;
assert expr2;
assert expr3;
}
This will eliminate the if block from the generated class file because the compiler is smart enough to see that it will never be executed. You can see the explanation of this form of conditional compilation at the end of this section of the Java Language Specification.
For a deeper look at the effects of assertions on performance, look at some of the posts on this StackOverflow thread.
Where to Avoid Assertions
Don't Use Assertions to Check Arguments in Public Methods
At last, a situation where assertions are not the best choice.
In theory, a public method can be used by everyone who has access to its class (although with the introduction of Java 9's module system, this may not be completely true). Most likely, they are part of your API.
For this reason, an assert statement is inappropriate. It can be disabled, but the method has to enforce the validation of its arguments at all times.
In addition, an assertion can only throw AssertionErrors. By manually checking the arguments, you have the option to use other exceptions or return an error code.
This practice doesn't apply to private methods because, in these cases, you have more control over what should happen.
Also, note that this practice doesn't say that you should not use assertions in public methods rather that you should not use them to check inputs to those methods.
Don't Use Assertions to Perform an Operation Required by Your Application
Having something like the following is bad practice because the newElement won't be added when assertions are disabled:
assert list.add(newElement);
The correct way to do it would be:
boolean elementAdded = list.add(newElement);
assert elementAdded;
Next Steps
In the next guide, Programming with Assertions in Java - Part 3, I'll talk about some misconceptions about assertions and wrap up the series.