Lab 13: Assertions and exceptions

Today, you'll be working with assertions and with exceptions, with particular attention to defining and throwing exceptions.

Clone the repository for today's lab:

hg clone /cslab/class/csci235/labs/lab13

I. Using assertions

We have frequently documented pre- and post-conditions for methods, and we have noted that if the caller does not satisfy the preconditions of a method, the behavior of the method is not defined. When a program does not work correctly, it would often be helpful to determine whether the fault lies in a class/method or in its caller. To assist in that, Java provides the assert statement, which consists of the reserved word assert followed by a boolean expression.

Normally, an assertion is treated like a comment, except for the fact that the expression must be able to be compiled. When you read a program with assertions, you know that a particular condition must be true at that point in the code. In addition, you can turn on checking of assertions when you run a program by giving the -enableassertions option on the Java command line. Java will then evaluate the boolean expression every time an assert statement is reached, and it will throw an AssertionError if the expression is not true. (Assertions are described briefly on pages 153-154 of the textbook.)

It can be especially helpful to write assertions for each method's preconditions. It is sometimes helpful to also assert postconditions, and, when the code is tricky, it may be helpful to assert things like loop invariants. In the first part of this lab, you will practice writing preconditions and assertions for them.

Change into the assert subdirectory for this lab. What you'll find there is a collection of classes that implement a slightly smarter array, supporting insertion, removal, and subarray, with a driver program for testing them.

What is missing in this case is the documentation of the preconditions for each of the methods in SmartArray. For each method in SmartArray, you need to

  1. Document appropriate preconditions, as described in the class's coding guidelines.
  2. Add assert statements that check these preconditions.

To see your assertions in action, you need to run the java command with the flag -enableassertions, as in

  java -enableassertions ArrayOps

You can abbreviate -enableassertions to -ea.

Exceptions

A. Defining and throwing exceptions

Change into the excepts directory. You will find there almost the same program. The key difference is that the methods of the SmartArray class in this version do not have preconditions; they are instead supposed to throw a SmartArrayException if they are called with bad arguments.

Take a look at SmartArrayException.java, which provides a minimal SmartArrayException class. There are several things to note about this definition:

Whenever you write an exception class, it will need to do these same things. You might provide other constructors if it makes sense to do so. If you want to get fancier (beyond the scope of this semester), you could replace other parts of the framework by writing additional methods.

In the SmartArray.java in this directory, the PRECONDITION lines are gone, but the methods that previously had preconditions have a throws clause to indicate that might throw a SmartArrayException. What you need to do now is to make each of those throw an appropriate exception if it is called with bad parameter values. You probably want to use the constructor that takes a string; make the message you pass something that will be helpful.

Once you see that it is working, you should commit all of your changes to your Mercurial repository.

B. Better exception handling (extra)

If you have time, modify code in ArrayOps to catch and handle the exceptions better. In particular, print a useful message and let the program continue.

Commit to your repository before proceeding.

C. Defining more exception classes (extra)

You can use the extends keyword to make additional classes that are subtypes of SmartArrayException. Doing that would allow the caller to distinguish among them by using multiple catch clauses.

You've been provided with AnotherSmartArrayException.java as an example of what this might look like. You could define your own classes that similarly extend SmartArrayException on a similar pattern. For example, you might distinguish between the cases of an index out-of-bounds (calling it, say, SmartArrayIndexException) and a bogus subrange (a SmartArraySubrangeException, when end is before start). The methods in SmartArray could then throw any of these exception classes.

The caller could catch the more specific case (or cases) by including an additional catch clause for the subtype. Note that because of the way Java matches exceptions to throw clauses, you should always order exceptions from more specific to more general (from subtypes to supertypes). A clause for AnotherSmartArrayException has been included in ArrayOps, even though no methods (currently) throw it.

So now you should be able to

And, of course, commit to Mercurial.

III. Turn in

To script your the execution of your programs

  1. Change into the top directory for this lab and start your typescript.
  2. Change into the asserts subdirectory, compile both source files, and the run the program several times with assertions enabled to show that it works for good input and fails appropriately for bad input.
  3. Now change into the except directory (cd ../excepts), compile all of the Java files (javac *.java), and again run your program several times to trip over a variety of the exceptions you throw.

After you end the script, make sure that you are in the top directory, and then

/cslab/class/csci235/bin/handin lab13 .
(note the dot!) to hand it all in.


Cary Gray
Last modified: Fri Nov 15 13:45:52 CST 2013