Return to lecture notes index
October 24, 2005 (Lecture 21)

Exceptional Control Flow

Let's think about the normal process involved when two object communicate. One object sends another object a message. That occurs by a method invocation. Then, after exhibiting some behavior, the receiving object replies. It does that by returning from the method call, and possibly by returning a value.

But, what happens when something out of the ordinary happens? For example, what happens if the method encounters an error and can't perform the requested actions? In C or Pascal, this is typically handled by using some special return value.

For example, in C, the convention is that all functions return "int" values. A return value of 0 indicates that the function completed successfully. Each negative return value generally indicates a different error. And, each positive return value generally indicates a non-error condition that prevented the function from completing. Since the return values are used to indicated the return type, values that are returned are generally managed as parameter's using C's "pass by address" mechanism.

But, this type of undocumented hackery is not necessary in C++ or Java. These languages support an "exception mechanism" that can be used to handle errors and other exception conditions -- both within a message and to communicate unhandled errors up the call chain.

As you'll learn today, Java's exception mechanism is far superior to the approach used by C, Pascal, and others. It can be used to remove error handling from the body of code, making the algorithmic component much cleaner. It is also much more self-documenting, because each type of error is represented by a meaningfully named class, not by a number. And, since it provides a different mechanism for returning in the exceptional case than the normal case, return values can be just that. All in all, it makes code more logical and more readable.

Representing an Error (Or Other Exceptional Condition)

In Java, errors or other exceptional conditions are represented with instances of the Exception class. An Exception is a reasonably simple class. For our purposes it has a two constructors, one of which take as String message, and one of which is the default constructor, which takes no parameters.

When an exception event, a.k.a, exception, occurs, the code that detects it creates a new Exception to represent the problem. When calling the constructor, it generally sets detailed information about the situation in plain-language, by passing it as the "message" to the constructor. When an Exception is converted to a String using toString(), it is this message that becomes part of the returned String.

Types of Exceptions

As we discussed moments ago, when errors or conditions were returned from C or Pascal functions, this return often took the form of a negative return value, with a different return value for each condition. The calling function would simply use a collection of if statements, or a switch/case statement to demultiplex the error conditions and take appropriate action to sort out the return value and take appropriate action. But, so far, in Java, we've only discussed one type of Exception. How are different conditions represented?

Java represents different types of Exceptions using inheritence. In Java, the generic Exception class can be extended to form different types of Exceptions. In fact, even the derived types are often extended to create even more specific types of exceptions.

Just to show an example, here is a very small piece of the Exception inheritence tree:

Creating Your Own Exceptions

You can create your own Exceptions, just by extending the Exception class. For example, let's consider the menu we wrote earlier this semester. What happens if the user makes a wrong choice? Retries too many times? Let's create Exceptions to model these situations.

  class MenuException extends Exception
  {
    public MenuException (String message)
    {
      super (message);
    }
  }
  

You probably haven't seen it before, but "super()" invokes the constructor of the super, a.k.a., parent class. In other words, the constructor for SetException will take a message as a String parameter and pass it to the constructor of the Exception class. Our function is now an Exception and handles a message exactly as does the generic type -- but, since we have a new type of extension, we will be able to tell what type of situation is present.

We can even take it one step further and create types of MenuException to represent the two separate options.

  class InvalidChoiceMenuException extends MenuException
  {
    public InvalidChoiceMenuException (String message)
    {
      super (message);
    }
  }
  

  class TooManyRetriesMenuException extends MenuException
  {
    public TooManyRetriesMenuException (String message)
    {
      super (message);
    }
  }
  

Using our prior, partial, tree as a basis, let's take a look at the Exception class hierarchy now:

Indicating An Exceptional Condition has Occurred

Now that we know what Exceptions are and how to declare them, how do we use them to indicate that an error has occured? Java indicates that Exceptions have occured by "throwing the Exception". When an Exception is thrown, control flow shifts from the normal control flow via if-statements, while loops, function calls and normal returns, &c to the exceptional control flow mechanism.

Next Class

...we'll learn to throw Exceptions and also to handle them, not surprisingly known as catching the Exception.