What are some real life examples to understand the key role of assertions?
Here's an assertion I wrote in a server for a Hibernate/SQL project. An entity bean had two effectively-boolean properties, called isActive and isDefault. Each could have a value of "Y" or "N" or null, which was treated as "N". We want to make sure the browser client is limited to these three values. So, in my setters for these two properties, I added this assertion:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
Notice the following.
This assertion is for the development phase only. If the client sends a bad value, we will catch that early and fix it, long before we reach production. Assertions are for defects that you can catch early.
This assertion is slow and inefficient. That's okay. Assertions are free to be slow. We don't care because they're development-only tools. This won't slow down the production code because assertions will be disabled. (There's some disagreement on this point, which I'll get to later.) This leads to my next point.
This assertion has no side effects. I could have tested my value against an unmodifiable static final Set, but that set would have stayed around in production, where it would never get used.
This assertion exists to verify the proper operation of the client. So by the time we reach production, we will be sure that the client is operating properly, so we can safely turn the assertion off.
Some people ask this: If the assertion isn't needed in production, why not just take them out when you're done? Because you'll still need them when you start working on the next version.
Some people have argued that you should never use assertions, because you can never be sure that all the bugs are gone, so you need to keep them around even in production. And so there's no point in using the assert statement, since the only advantage to asserts is that you can turn them off. Hence, according to this thinking, you should (almost) never use asserts. I disagree. It's certainly true that if a test belongs in production, you should not use an assert. But this test does not belong in production. This one is for catching a bug that's not likely to ever reach production, so it may safely be turned off when you're done.
BTW, I could have written it like this:
assert value == null || value.equals("Y") || value.equals("N") : value;
This is fine for only three values, but if the number of possible values gets bigger, the HashSet version becomes more convenient. I chose the HashSet version to make my point about efficiency.
Here's another example. I wrote a method that finds the median of the values in two sorted arrays. The method assumes the arrays are already sorted. For performance reasons, it should NOT sort the arrays first, or even check to ensure they're sorted. However, it's a serious bug to call this method with unsorted data, and we want those bugs to get caught early, in the development phase. So here's how I handled those seemingly conflicting goals:
public static int medianOf(int[] a, int[] b) {
assert assertionOnlyIsSorted(a); // Assertion is order n
assert assertionOnlyIsSorted(b);
... // rest of implementation goes here. Algorithm is order log(n)
}
public static boolean assertionOnlyIsSorted(int[] array) {
for (int i=1; i<array.length; ++i) {
if (array[i] < array[i-1]) {
return false;
}
return true;
}
}
This way, the test, which is slow, is only performed during the development phase, where speed is less important than catching bugs. You want the medianOf()
method to have log(n) performance, but the "is sorted" test is order n. So I put it inside an assertion, to limit its use to the development phase, and I give it a name that makes it clear it's not suitable for production.
This way I have the best of both worlds. In development, I know that any method that calls this incorrectly will get caught and fixed. And I know that the slow test to do so won't affect performance in production. (It's also a good illustration of why you want to leave assertions off in production, but turn them on in development.)
A lot of good answers explaining what the assert
keyword does, but few answering the real question, "when should the assert
keyword be used in real life?"
The answer: almost never.
Assertions, as a concept, are wonderful. Good code has lots of if (...) throw ...
statements (and their relatives like Objects.requireNonNull
and Math.addExact
). However, certain design decisions have greatly limited the utility of the assert
keyword itself.
The driving idea behind the assert
keyword is premature optimization, and the main feature is being able to easily turn off all checks. In fact, the assert
checks are turned off by default.
However, it is critically important that invariant checks continue to be done in production. This is because perfect test coverage is impossible, and all production code will have bugs which assertions should help to diagnose and mitigate.
Therefore, the use of if (...) throw ...
should be preferred, just as it is required for checking parameter values of public methods and for throwing IllegalArgumentException
.
Occasionally, one might be tempted to write an invariant check that does take an undesirably long time to process (and is called often enough for it to matter). However, such checks will slow down testing which is also undesirable. Such time-consuming checks are usually written as unit tests. Nevertheless, it may sometimes make sense to use assert
for this reason.
Do not use assert
simply because it is cleaner and prettier than if (...) throw ...
(and I say that with great pain, because I like clean and pretty). If you just cannot help yourself, and can control how your application is launched, then feel free to use assert
but always enable assertions in production. Admittedly, this is what I tend to do. I am pushing for a lombok annotation that will cause assert
to act more like if (...) throw ...
. Vote for it here.
(Rant: the JVM devs were a bunch of awful, prematurely optimizing coders. That is why you hear about so many security issues in the Java plugin and JVM. They refused to include basic checks and assertions in production code, and we are continuing to pay the price.)
Assertions are disabled by default. To enable them we must run the program with -ea
options (granularity can be varied). For example, java -ea AssertionsDemo
.
There are two formats for using assertions:
assert 1==2; // This will raise an AssertionError
.assert 1==2: "no way.. 1 is not equal to 2";
This will raise an AssertionError with the message given displayed too and is thus better. Although the actual syntax is assert expr1:expr2
where expr2 can be any expression returning a value, I have used it more often just to print a message.assert
is a keyword. It was introduced in JDK 1.4. The are two types of assert
s
assert
statementsassert
statements.By default all assert
statements will not be executed. If an assert
statement receives false, then it will automatically raise an assertion error.
What does the assert keyword in Java do?
Let's look at the compiled bytecode.
We will conclude that:
public class Assert {
public static void main(String[] args) {
assert System.currentTimeMillis() == 0L;
}
}
generates almost the exact same bytecode as:
public class Assert {
static final boolean $assertionsDisabled =
!Assert.class.desiredAssertionStatus();
public static void main(String[] args) {
if (!$assertionsDisabled) {
if (System.currentTimeMillis() != 0L) {
throw new AssertionError();
}
}
}
}
where Assert.class.desiredAssertionStatus()
is true
when -ea
is passed on the command line, and false otherwise.
We use System.currentTimeMillis()
to ensure that it won't get optimized away (assert true;
did).
The synthetic field is generated so that Java only needs to call Assert.class.desiredAssertionStatus()
once at load time, and it then caches the result there. See also: What is the meaning of "static synthetic"?
We can verify that with:
javac Assert.java
javap -c -constants -private -verbose Assert.class
With Oracle JDK 1.8.0_45, a synthetic static field was generated (see also: What is the meaning of "static synthetic"?):
static final boolean $assertionsDisabled;
descriptor: Z
flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
together with a static initializer:
0: ldc #6 // class Assert
2: invokevirtual #7 // Method java/lang Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #2 // Field $assertionsDisabled:Z
16: return
and the main method is:
0: getstatic #2 // Field $assertionsDisabled:Z
3: ifne 22
6: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
9: lconst_0
10: lcmp
11: ifeq 22
14: new #4 // class java/lang/AssertionError
17: dup
18: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
We conclude that:
assert
: it is a Java language conceptassert
could be emulated pretty well with system properties -Pcom.me.assert=true
to replace -ea
on the command line, and a throw new AssertionError()
.