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.
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!
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!
// 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.
// 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 < 0which 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.
// 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".
Other rules on specification: