In the last three posts, we looked at a few different ways to walk a file tree. Let’s leave file walking behind, and look at a rarely used, but very useful feature of the Java language, namely assertions.
Java 1.4 introduced assertions with the keyword assert
.
What is an assertion?
An assertion is a statement that lets us test any assumptions that we have about our code.
Let’s say we have to write a method that calculates the square root of a number. To test that it won’t fail, we might want to assert that the number is not negative. To test that it works properly we might want to check that we get the original number when the resulting square root is multiplied by itself.
Writing assertions is a fast, easy way to discover incorrect application behaviour. Assertions can be used to document the inner workings of a program. This helps make the application more maintainable.
Assertions are useful in many places where we would have added comments to document that something should, or shouldn’t, happen. For example, if we’ve left out a default
case in a switch
statement because the program can’t possibly get there normally. Or we’ve assumed a variable should have a constant value and we’ve coded with that in mind.
The assertion statement supports an informal approach to the Design by Contract methodology. With Design by Contract we first define what our method is supposed to do, and then we verify its behaviour by testing it during execution. We can test preconditions, post-conditions and invariants with assertions.
Using assertions can increase the size of our code slightly, but in the end we can often deliver a product in a much shorter development time. The earlier we can fix bugs, the lower the development cost. Using assertions doesn’t guarantee bug-free code, but it gets us closer to it than code without assertions.
These assertions are very different to the JUnit testing framework assertXxx()
methods. That will be the topic of a future post.
Assertion Syntax
Each assertion contains and tests a boolean
expression that we believe to be true when the assertion executes. If the expression evaluates to false, the system throws an AssertionError
. If the expression is true, then the program continues normally. The assertion confirms any assumptions we may have about the program behaviour. This increases our confidence that there are no errors in our application.
The assertion statement has two forms. The first form is the simplest:
assert boolean_expression;
When the system executes the assertion, it evaluates the boolean_expression
. If it is false
, an AssertionError
is thrown with no detail message.
The second form of the assertion statement is:
assert boolean_expression : value_expression;
The value_expression
is an expression that evaluates to a value (not void
). The value is converted to a string that is passed as the detail message to the AssertionError
constructor. This is like the way we usually create exceptions with detail messages. We often just use a String
message as the value.
The message gives more information on why the assertion failed. This will then help us diagnose and fix the problem. The assertion message is meant for the developers, not for the users. We must always interpret the assertion message in the context of a full stack trace.
Assertions should never be triggered in production code. They are for the development/debug/test parts of the production lifecycle. Assertions point to bugs or a misuse of a code path. An AssertionError
indicates unrecoverable conditions in an application. We must never try to handle them or attempt recovery.
Assertion Examples
The following simple example shows how we can use an assertion in the default
case of a switch
statement:
default: assert false : "Unknown value";
Another way could be to throw an AssertionError
. This will give protection even if assertions are disabled:
default: throw new AssertionError("Unknown value");
Here’s another example. We often write code like the following:
Connection conn = DriverManager.getConnection();
if (conn == null) {
throw new RuntimeException("Connection is null.");
}
Using assertions, we can change the code to:
Connection conn = DriverManager.getConnection();
assert conn != null : "Connection is null.";
Enabling Assertions
By default, assertions are disabled at runtime. Disabling assertions largely eliminates their performance penalty. There are still some small performance issues related to optimization of JIT inline compiled code.
We use the JVM command-line options/switches to enable or disable assertions:
- To enable assertions, use
-ea
or–enableassertions
. - To disable assertions, use
-da
or-disableassertions
. - To enable assertions in system classes, use
–esa
or-enablesystemassertions
. - To disable assertions in system classes, use
–dsa
or–disablesystemassertions
.
We can pass additional arguments to the options to specify different levels of granularity. We can enable or disable assertions in all classes except system classes; for classes in the named package and any sub-packages; for classes in the unnamed package; and in the named class.
For further information, consult the Java tools documentation.
Best Practices
The most important thing to remember is that assertions can be disabled at runtime. We must never assume that they will always be executed. We must never use an assertion to do anything that our application needs to operate correctly.
Keep the following in mind when using assertions:
-
Always check for null values and empty
Optional
s where appropriate. -
Avoid using assertions to check inputs into a public method. Rather use an unchecked exception such as
IllegalArgumentException
orNullPointerException
. -
Don’t call methods in assertion conditions. Rather assign the result of the method to a local variable and use that variable with
assert
. -
Assertions are good for sections in code that will never be executed, such as the default case of a switch statement or after a loop that never ends.
Conclusion
Have you used assertions to check your code? Did it make it easier to write your application? Please share your comments on the blog post.
Stay safe and keep learning!