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.
In some cases, assertions are the way to go!
For documentation purposes, try something like the following:
1void printTotals() {
2 // ...
3 String amountStr = amountToString(amount);
4 // amountStr should not be null
5 System.out.println("Amount: " + amountStr);
6 // ...
7}
It's even better to enforce this check when testing or running a program. Assertions can highlight potential bugs:
1void printTotals() {
2 // ...
3 String amountStr = amountToString(amount);
4
5 // amountStr should not be null
6 assert amountStr != null;
7
8 System.out.println("Amount: " + amountStr);
9 // ...
10}
Never again rely on using comments and a debugger. Use assertions instead!
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:
1switch(type) {
2 case 1:
3 //...
4 break;
5 case 2:
6 //...
7 break;
8 case 3:
9 //...
10 break;
11 default:
12 assert false : "Default case reached";
13}
However, you must take into account that truly unreachable code can generate a compile-time error:
1if(interest < 0) {
2 return 0;
3} else {
4 return interest;
5}
6
7assert false : "A valid interest should be returned"; // Compile time error
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:
1String defaultPassw = generateDefaultPassword();
2
3assert 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.
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:
1static final boolean assertsEnabled = false;
2
3// ...
4
5if (assertsEnabled) {
6 assert expr1;
7 assert expr2;
8 assert expr3;
9}
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.
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.
Having something like the following is bad practice because the newElement
won't be added when assertions are disabled:
1assert list.add(newElement);
The correct way to do it would be:
1boolean elementAdded = list.add(newElement);
2assert elementAdded;
In the next guide, Programming with Assertions in Java - Part 3, I'll talk about some misconceptions about assertions and wrap up the series.