Notes for June 24, 2005

Exceptions

From a Java tutorial off of Sun's main page:

The term exception is shorthand for the phrase "exceptional event."

Definition: An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions during the execution of a program.


When a user does something wrong, the computer should recover gracefully instead of falling apart. Programmers flag the error by throwing exceptions when the error occurs. Somewhere else in the program, an exception handler will catch the exception and do the appropriate actions to recover. For now, we are only interested in throwing the exception, i.e., flagging that something erroneous has occurred.

How does one throw an exception? Just use the syntax:

throw new <EXCEPTION_NAME>("<MESSAGE>");

As an example, the IntList class should not instantiate an object if the client enters a negative capacity. What does it mean for a list to have a negative capacity anyway? In the constructor, the code simply checks to see if the capacity argument is negative. If so, it throws an exception.

    // pre : capacity >= 0
    // post: constructs an empty list with the given capacity
    public IntList(int capacity) {
	if (capacity < 0)
	    throw new IllegalArgumentException("negative capacity");
	this.elementData = new int[capacity];
	this.size = 0;
    }

Now, if the client code tries to construct an IntList with a negative capacity:

    IntList list = new IntList(-1);
the program will halt with something similar to:
    Exception in thread "main" java.lang.IllegalArgumentException: negative capacity
            at IntList.<init>(IntList.java:61)
            at IntListSample.main(IntListSample.java:5)
The numbers indicate exactly where the program threw the exception. This is not a very graceful way to recover. Remember, for now, we will not cover exception handling, only exception throwing.

You can also have a specific method that checks the precondition of a method. For example, the get method has a precondition that the index argument is greater-than or equal to 0 and less than size. It makes a call to checkIndex to verify that precondition. We re-use that method inside remove and set.

    // post: throws an exception if the given index is out of range
    private void checkIndex(int index) {
	if (index < 0 || index >= this.size)
	    throw new IndexOutOfBoundsException("illegal index");
    }

    // pre : 0 <= index < size()
    // post: returns the integer at the given index in the list
    public int get(int index) {
	checkIndex(index);
	return this.elementData[index];
    }

    // pre : 0 <= index < size()
    // post: removes value at the given index, shifting subsequent values left
    public void remove(int index) {
	checkIndex(index);
	for (int i = index; i < this.size - 1; i++)
	    this.elementData[i] = this.elementData[i + 1];
	this.size--;
    }

Debugging tip: You can also use exceptions to debug your code. For example, you can use the following code to verify that your array is sorted:

    private void verifySorted() {
        for (int i = 0; i < size-1; i++) {
            if (elementData[i] > elementData[i+1]) {
                throw new RuntimeException("not sorted");
            }
        }
    }

Now, call verifySorted at various places in your code (for example, in the add method or as the first line to indexOf) while testing your code. Whenever the program halts, you can easily trace to where the error occurred provided that you placed the call to verifySorted judiciously. Don't forget to remove all calls to verifySorted after you are done with testing. If you leave it in indexOf, it will slow down that call and not take advantage of the efficiency of binary search. Please note that this only verifies "sortedness". It does not verify that the elements actually made it into the array. For example, if your add just throws away the input, it will always remain sorted!

Boolean Zen

Beginning students tend to not believe in boolean values. They can easily believe in numbers, but somehow true and false eludes them. For example, if you have identity method that returns an int, you would write the following code:
    public int identity(int x) {
        return x;
    }
You would NOT write:
    public int identity(int x) {
        if (x == 1) {
            return 1;
        } else if (x == 2) {
            return 2;
        } else if (x == 3) {
            return 3;
        } else if (x == 4) {
            return 4;
        } ...
    }
x is an int and thus can be returned right away. So why is it that if a beginning student were to write an isEmpty method, they tend to write it as:
     public boolean isEmpty() {
         if (size == 0) {
             return true;
         } else {
             return false;
         }
     }
The expression (size == 0) already gives you a boolean value! The above code is redundant, unnecessary, and will lead to points off! A better way to write it would be:
    // post: returns true if list is empty, false otherwise
    public boolean isEmpty() {
	return this.size == 0;
    }
If you do not believe in boolean values, how far would you go? Why not write:
     public boolean isEmpty() {
         if ((size == 0) == true) {
             return true;
         } else {
             return false;
         }
     }
or what about:
     public boolean isEmpty() {
         if (((size == 0) == true) == true) {
             return true;
         } else {
             return false;
         }
     }
You can easily see how absurd and impossible this gets. As a final note: believe in booleans!

private

What is interesting about the following method?
    // post: appends all values in the given list to the end of this list
    public void addAll(IntList other) {
	for (int i = 0; i < other.size; i++)
	    this.add(other.elementData[i]);
    }
addAll does not call other.size(). Instead, it directly accesses the private data field size. The same applies to elementData[i]. This works, because in Java, private means "private to the class" and not "private to the object". Methods within a class can access the private data fields of objects of the same class.

clear

The clear method is very interesting in that is very short. It does not reset the values in the array. It just changes the size variable to zero.
    // post: list is empty
    public void clear() {
	this.size = 0;
    }
Why does this work? Couldn't the client code access the other non-zero values in the array. The answer, of course, is no. The reason is because the get method checks that the index given is valid. To be a valid index, it must be:
    // pre : 0 <= index < size()
which is equivalent to saying:
    // pre : 0 <= index < 0
which never holds! Thus, a user cannot access any part of the array when the array was just cleared. When an element is added, then the client code can only access that one element, because now size has incremented to 1.

This leads to a very important distinction between the capacity of the list and the size of the list. The capacity states how many elements can eventually fit in the list, while the size signifies how many elements are valid.

this

Make sure you understand the two uses of this. this followed by parentheses calls another constructor. This can only be done within a constructor. this followed by a period refers to the object which the method is acting upon. Many times, it can be omitted. For example,
    // post: returns true if the given value is contained in the list,
    //       false otherwise
    public boolean contains(int value) {
	return this.indexOf(value) != -1;
    }
can be changed to:
    // post: returns true if the given value is contained in the list,
    //       false otherwise
    public boolean contains(int value) {
	return indexOf(value) != -1;
    }
When there is no ambiguity, this. can be omitted, because it is implicitly there. This is known as an implicit parameter. More details about this are in the reading on "Classes".

Homework

Make sure add works! The grading program will break down if your add does not work!

Other rules on specification:

  • Do not have public helper methods.
  • Do not have unused private helper methods.
  • Do not have extraneous public methods. If the specifications do not mention it, do not implement it. Your SortedIntList should not implement a video game inside it.
  • FOLLOW THE SPECIFICATIONS!