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
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
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.
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:
Exception on the end of
classes that are exceptions.
extends Exception. This similar to the
way that a class can implement an interface, but the
supertype here is a class instead of an interface. The class that
we extend will usually be one of Exception,
RuntimeException, or Error.
String that contains a
message about the exception.
super() does.
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.
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.
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
SmartArray to
throw the more specific type; and
ArrayOps to catch the more specific type as
well as SmartArrayException.
And, of course, commit to Mercurial.
To script your the execution of your programs
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.
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.