15-100 Lecture 9 (Wednesday, June 4, 2008)

Nested Exceptions

Last class, when we created our OutOfBoundsException, we created it as an independent class. Creating a new, independent class for an exception, like creating any new independent class, has some interesting consequences. One of these is that another class, with the same name, can't exist within the same program (or, at the least namespace, such as package).

For the most part, this is fine. How many String classes do we need? But, in some cases, it can be a problem. For example, several different classes might define OutOfBoundsException exceptions. If these classes are used together, there will be a problem -- the identfier names will collide. In other words, when we refer to the OutOfBoundsException class, which one we mean is ambiguous.

We can solve this problem by "nesting" the exception classes within the class that they server. When we "nest" one class within another, we sometimes refer to the "nested" class as the "inner class" and the encompassing class as the "outer class".

By doing this, we place the inner class within the namespace of the outer class. So, in our example of yesterday, the OutOfBoundsException class becomes known as the ExceptionExample.OutOfBoundsException class. Notice the use of the .-scope operator to show that the OutOfBoundsException is within the ExceptionExample class.

When we do this, we need to clarify the access of the inner class, just as we would an instance variable. If the class is qualified as "private", it can only be used within the outer class. This implies that only private methods can throw this type of Exception. Public methods cannot throw nested private exceptions, because there is no way for a caller outside of the class to catch them -- the definition, itself, is private.

So, if an exception is defined by a private inner class, it can be used within any method or thrown out of other private methods -- but never thrown out of public methods.

When an exception defined by an inner class is used within the encompassing our class, it need not be fully qualified with the outer class's name. So, for example, when using OutOfBoundsException within ExceptionExample, it is correct to refer to it either as ExceptionExample.OutOfBoundsExample or, simply, as OutOfBoundsException. But, if it is caught outside of the outer class, it must be identified by its fully-qualified name, ExceptionExample.OutOfBoundsException.

Nested Exception Example From Class

  /* ------- MagicNumber.java ------- */
  import java.util.*;
  import java.io.*;


  class MagicNumber {
 
    private magicNumber;

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


    public MagicNumber() {
      magicNumber = -1;
    }


    private void verifyRangeInclusive(int candidate, int lower, int upper) {
      if ( (candidate < lower) || (candidate > upper))
        throw new OutOfBoundsException ("" + candidate + " is out of range " +
                                        lower + " - " + upper + ", inclusive.");
    }

    public int getMagicNumber() {
      return magicNumber;
    }


    public void askForMagicNumber () throws OutOfBoundsException { 
      Scanner keyboard = new Scanner(System.in);
    
      System.out.print ("Enter an int: ");
    
      try {
        magicNumber = keyboard.nextInt();
        verifyRangeInclusive(intNumber, 1, 10);
        System.out.println ("The number was: " + intNumber);
      }
      catch (InputMismatchException ime) {
        System.out.println ("Sorry -- that wasn't a number.");
      }
    }
  }


  /* ------- ExampleProgram.java ------- */
  import java.util.*;
  import java.io.*;


  class ExampleProgram {
    public static void main (String[] args) {
      MagicNumber mn = new MagicNumber();

      try {
        mn.askForMagicNumber();
      } 
      catch (MagicNumber.OutOfBoundsException oobe) {
        System.out.println (oobe.getMessage()); 
      }
    }
  }
  

The String class

We spoke a bit about the String class. You were reminded that Strings are immutable and, specifically, that the methods that might otherwise seem to mutate a String actually return a reference to a new String.

After that, we talked about some of the commonly used String methods. But, I promised you that I would not include that discussion within the lecture notes. Instead I encouraged you to look it up in the Java API Documentation. I further suggested that you copy down the signatures for each of them by hand and make notes about anything interesting about the arguments or returns. You don't need to worry about the obscure ones, or those involving patterns -- instead focus on the simple stuff, such as toUpperCase(), substring(...), concat(...), &c. The goal is for you to remember those that you are likely to use.

I also warned you to look at substring(...) very carefully. The arguments are indexes -- but may not work as you expect. Please read the documentation carefully -- and give it a try to make sure you get it. Try a couple of examples of your own choosing.