Postcondition checks are best implemented via assertions, whether or not they are specified in public methods. For example:
/**
* Returns a BigInteger whose value is (this-1 mod m).
*
* @param m the modulus.
* @return this-1 mod m.
* @throws ArithmeticException m <= 0, or this BigInteger
* has no multiplicative inverse mod m (that is, this BigInteger
* is not relatively prime to m).
*/
public BigInteger modInverse(BigInteger m) {
if (m.signum <= 0)
throw new ArithmeticException("Modulus not positive: " + m);
if (!this.gcd(m).equals(ONE))
throw new ArithmeticException(this + " not invertible mod " + m);
... // Do the computation
assert this.multiply(result).mod(m).equals(ONE);
return result;
}
|
In practice, one would not check the second precondition (this.gcd(m).equals(ONE)) prior to performing the computation, because it is wasteful. This precondition is checked as a side effect of performing the modular multiplicative inverse computation by standard algorithms.
Occasionally, it is necessary to save some data prior to performing a computation in order to check a postcondition after it is complete. This can be done with two assert statements and the help of a simple inner class designed to save the state of one or more variables so they can be checked (or rechecked) after the computation. For example, suppose you have a piece of code that looks like this:
void foo(int[] array) {
// Manipulate array
...
// At this point, array will contain exactly the ints that it did
// prior to manipulation, in the same order.
}
|
Here is how you could modify the above method to turn the textual assertion into a functional one:
void foo(final int[] array) {
class DataCopy {
private int[] arrayCopy;
DataCopy() { arrayCopy = (int[])(array.clone()); }
boolean isConsistent() { return Arrays.equals(array, arrayCopy); }
}
DataCopy copy = null;
// Always succeeds; has side effect of saving a copy of array
assert (copy = new DataCopy()) != null;
... // Manipulate array
assert copy.isConsistent();
}
|
Note that this idiom easily generalizes to save more than one data field, and to test arbitrarily complex assertions concerning pre-computation and post-computation values.
The first assert statement (which is executed solely for its side-effect) could be replaced by the more expressive:
copy = new DataCopy(); |
but this would copy the array whether or not asserts were enabled, violating the dictum that asserts should have no cost when disabled.