I’ve seen this question floating around a bit on the web so I thought I’d shed some light on the topic since it was bothering me for a little while also.
What’s the difference between assert
and AssertionError
?
People often ask what’s the difference between assert
and manually throwing an
AssertionError
given that an assert statement will throw the AssertionError
anyway?
Learning by Example
Let’s look at a super short example to paint a better picture:
/**
* Example 1: assert statement, this is a totally goofy example but bear with me.
*/
public class Test {
public static void main(String[] args) {
assert false;
}
}
Compile and run
javac Test.java && java Test
You may (or may not) be surprised to see no output. This is because java is
ignoring your assertions. Simply re-run your application with assertions
enabled, using the -ea
switch:
java -ea Test
Exception in thread "main" java.lang.AssertionError
at Test.main(Test.java:3)
Now let’s try throwing an AssertionError
manually and see what happens:
/**
* Example 2: Using AssertionError()
*/
public class Test {
public static void main(String[] args) {
throw new AssertionError();
}
}
Build & run:
javac Test.java && java Test
Exception in thread "main" java.lang.AssertionError
at Test.main(Test.java:3)
It seems like if we throw an AssertionError
it guarantees that our program
will crash. You might be wondering how that could possibly be a good thing,
but first let me explain the semantics behind each.
Semantics: The Real Difference
The Java document Programming With Assertions states,
An assertion is a statement in the Java programming language that enables you to test your assumptions about your program.
It goes on to exemplify,
…if you write a method that calculates the speed of a particle, you might assert that the calculated speed is less than the speed of light.
While I’m not 100% sure about that science mambo-jambo, although it sound pretty legit to me I guess, the docs explain that assertions are intended to verify the assumptions that you make about your own code.
Each assertion contains a boolean expression that you believe will be true when the assertion executes. If it is not true, the system will throw an error. By verifying that the boolean expression is indeed true, the assertion confirms your assumptions about the behavior of your program, increasing your confidence that the program is free of errors.
Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability.
You can read the rest of their documentation about assertions (same link as above) if you’re interested to learn more about how to use them.
Manually throwing an AssertionError
, by contrast, is making a statement to
anyone using your code (including yourself) that one of the assumptions you
have made about your code has been violated. In other words, you done goofed.
AssertionError
is a subclass of Error
.
Which means we’ve hit an unrecoverable error and we shouldn’t try to catch it.
The program is forced to crash as a metaphoric slap on the wrist to tell the
developer they have screwed up and they need to mend their ways.
From the Error javadoc:
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions
So it sounds like, semantically, we should throw an AssertionError
whenever
we see fit to tell the users of our code that they have attempted to use our
code in a way it was not intended for.
Learning by Example, Revisited.
So wait… Why do we want our program to crash again?
Now like I said, it’s to protect the developers who are using our code (including ourselves) from doing something wrong with our code. Let’s look at some real-world examples where it might make sense to do this.
Let’s say you have a utility class with all static methods, you might want to enforce noninstantiability of a class by throwing such an error in the constructor:
// String utility class that does some common things with strings
public class MrStringUtility {
// force non-instantiability through private method
private MrStringUtility() {
throw new AssertionError();
}
// ... some other string methods here
}
You may be wondering why is it an AssertionError
and not an
IllegalArgumentException
. Throwing an Exception
hints that we could just
catch the exception and deal with it, which is not what we want. The last thing
we need is people trying to forcibly use our code in ways it wasn’t designed
and letting them get away with it — Oh-ho-ho!
In this example two steps are taken to prevent instantiation. The first is the
use of the private
visibility modifier (Josh Bloch, Item 4), this will
prevent outside classes from accessing this constructor from conventional
means. The second is the use of the AssertionError
in the constructor which
in itself serves two purposes: 1) it prevents instantiation of the class and 2)
it signals to the troublemakers who have weaseled their way into this
constructor that this class is not meant to be instantiated.
Another scenario in which you might want to see this in in a switch statement
where you are fairly certain that there are only n possible options. Rather
than omitting a default
case, you can add one with an AssertionError()
.
public class MrRockPaperScissors {
// assuming some enum has been defined for Rock, Paper, Scissors
// outputs the winner of the game or some such
public void determineWinner() {
switch (playerChoice) {
case Hand.Rock:
// ...
break;
case Hand.Paper:
// ...
break;
case Hand.Scissors:
// ...
break;
default:
throw new AssertionError();
}
}
// ... some other code ...
}
Conclusion
So when is it really appropriate to use an AssertionError
over a regular
assert
? An AssertionError
as we’ve seen is more powerful than a regular
assertion since it is guaranteed to explode, therefore it is more appropriate
to use when we want to get a clear message across that something happened that
should not be possible like instantiating classes that are not expected to be
instantiated.