Using AssertionsS2C Home « Using Assertions
In this lesson we learn about the Assertion mechanism and how to write and then enable any assertion code within our programs. Assertions are a mechanism, mainly used in development, that allow us to check conditions
that we expect to be true
. We use the assert
keyword for this purpose and if the condition returns true
as expected then normal program flow continues. If, however, the condition
returns false
an assertion error is thrown.
Using the Assertion mechanism has no 'hit' on performance as we enable assertions at runtime using the -ea
option. Compilation of a source file that uses assertions is the same as normal and any assertion code
is ignored at runtime in default mode unless the file is run using the -ea
option. So as an example:
javac OurProgram.java // Compile normally
java OurProgram // Run normal default mode
java -ea OurProgram // Run enable assertions mode
We would compile and run the OurProgram
class in normal default mode. Then we would run the program with assertions enabled, and assertion code would be invoked as part of the block of code it belongs to. The
assertion code leaves no footprint and is just ignored when the class is run without the -ea
option.
Appropriate Assertion UsageTop
Assertions should not be used to validate command-line arguments. We would have to run the code everytime using the -ea
option to ensure assertions run the validation. Using exceptions is
more appropriate for validating command-line arguments as these run regardless of deployment and the use of the -ea
option.
Assertions should never be used that cause side-effects such as changing a value used elsewhere in the code. If we do so we are reliant on code being run using the -ea
option to get the values changed. What
happens when someone else runs the code without assertions enabled! Unreliable results and a difficult bug to find, that isn't really a bug, just an incorrect use of the assertion mechanism.
When considering whether to use assertions to validate arguments to a method, we need to consider the access modifier of the method:
- Methods marked as
public
are not considered appropriate for assertions;public
methods are available to anyone and should be robust enough to guarantee the validation and robustness of their interfaces. Using exceptions is more appropriate forpublic
methods as these run regardless of deployment and the use of the-ea
option. - Methods marked as
protected
are not considered appropriate for assertions;protected
methods are available to subclasses outside the package and so should be robust enough to guarantee the validation and robustness of their interfaces. Using exceptions is more appropriate forprotected
methods as these run regardless of deployment and the use of the-ea
option. - Methods with no access modifier are appropriate for assertions if you control the package;
package-private
methods are available only to programs within the package they belong to. If you have control then you can be reasonably assured that logic calling yourprotected
method is correct. - Methods marked as
private
are considered appropriate for assertions as you control the code that calls the method;private
methods are available only to the class they are written in. Therefore you have control and can be reasonably assured that logic calling yourprivate
method is correct.
Assertions should be used anywhere in code when you have code that should never be reached, such as a switch default
statement, that you know will never be invoked. In these situations you set the assertion
mechanism to false
rather than test a condition. This ensures that if the statement is ever reached, when assertions are enabled, we will get an AssertionError
.
Using the assert
KeywordTop
The following table shows the two forms of the assert
statement which are only active when running our code with the -ea
option. The second form just allows us to pass more information to an
AssertionError and we must ensure that the expression returns a value.
Assert Form | Example | Description |
---|---|---|
form1 | ||
assert condition; // Other code | assert (a > 0); | Throw AssertionError if condition equates to false Run this code when assertion equates to true . |
form2 | ||
assert condition: expression; // Other code | assert (a > 0): "a = " + a; | Throw AssertionError if condition equates to false passing the value returned from expression .Run this code when assertion equates to true . |
A private
Method ExampleTop
The following code is an appropriate use of an assertion to validate arguments to a private method:
/*
A class using Assertions correctly
*/
class A {
String firstName;
String lastName;
A(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
printName(firstName, lastName);
}
private void printName(String firstName, String lastName) {
assert (firstName != "");
assert (lastName != "");
System.out.println("First name of: " + firstName);
System.out.println("Last name of: " + lastName);
}
}
Save and compile and the A
class in directory c:\_FlowControl in the usual way. Now we need to write a test class to make sure the A
class works as intended.
/*
Test A class
*/
public class TestA {
public static void main(String[] args) {
A a = new A("John", "Smith");
A b = new A("", "Smith");
}
}
Save, compile and run the TestA
test class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running the TestA
class. We passed an empty firstName
string to the printName(String firstName, String lastName)
method and the program ended successfully.
Run the TestA
test class in directory c:\_FlowControl with the -ea
option.
The above screenshot shows the output of running the TestA
class with assertions enabled. We passed an empty firstName
string to the printName(String firstName, String lastName)
method and the program ends with an AssertionError
.
Non-reachable Code ExampleTop
The following code is an appropriate use of an assertion to throw an AssertionError
when we encounter code that should never be reached:
/*
A class using Assertions correctly
*/
class B {
int i;
B(int i) {
this.i = i;
// A switch where a case matches.
switch (i) {
case 1:
System.out.println("1 matched");
break;
case 2:
System.out.println("2 matched");
break;
case 3:
System.out.println("3 matched");
break;
// Something went badly wrong to get here!!
default: assert false : "Value of: " + i + " passed to constructor.";
}
}
}
Save and compile and the B
class in directory c:\_FlowControl in the usual way. Now we need to write a test class to make sure the B
class works as intended.
/*
Test B class
*/
public class TestB {
public static void main(String[] args) {
B a = new B(1);
B b = new B(4);
}
}
Save, compile and run the TestB
test class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running the TestB
class. We passed a value of 4
to the B
constructor and the program ended successfully.
Run the TestA
test class in directory c:\_FlowControl with the -ea
option.
The above screenshot shows the output of running the TestB
class with assertions enabled. We passed a value of 4
to the B
constructor and the program ends with an AssertionError
.
Lesson 5 Complete
In this lesson we learnt about the Assertion mechanism and how to write and then enable any assertion code within our programs.
What's Next?
We begin the API Contents section with an investigation of the String
Class.