Jump to content

Nesting Exceptions

75% developed
From Wikibooks, open books for an open world

Navigate Exceptions topic:v  d  e )


When an exception is caught, the exception contains the stack-trace, which describes the error and shows where the exception happened (i.e. where the problem is and where the application programmer should look to fix the problem). Sometimes it is desirable to catch an exception and throw another exception. If the new exception keeps a reference to the first exception, the first exception is called a nesting exception.

Computer code Code listing 6.4: NestingExceptionExample.java
public class NestingExceptionExample {
 
  public static void main(String[] args) throws Exception {
    Object[] localArgs = (Object[]) args;
   
    try {
      Integer[] numbers = (Integer[]) localArgs;
    } catch (ClassCastException originalException) {
      Exception generalException = new Exception(
        "Horrible exception!",
        originalException);
      throw generalException;
    }
  }
}
Standard input or output Output for Code listing 6.4
Exception in thread "main" java.lang.Exception: Horrible exception!
at NestingExceptionExample.main(NestingExceptionExample.java:9)
Caused by: java.lang.ClassCastException: [Ljava.lang.String; incompatible with [Ljava.lang.Integer;
at NestingExceptionExample.main(NestingExceptionExample.java:7)

The above code is an example of a nesting exception. When the Exception is thrown, by passing in the ClassCastException object reference as a parameter, the ClassCastException is nested in the newly created Exception, its stack-trace is appended together. When the Exception is caught, its stack-trace contains the original ClassCastException's stack-trace.

This is a kind of exception conversion, from one exception to another. For example, calling a remote object using RMI, the calling method has to deal with RemoteException which is thrown if something is wrong during the communication. From the application point of view, RemoteException has no meaning, it should be transparent to the application that a remote object was used or not. So the RemoteException should be converted to an application exception.

This conversion can also hide where the error is originated. The stack-trace starts when the exception is thrown. So when we catch and throw a new exception, the stack-trace starts at when the new exception was thrown, losing the original stack-trace. This was true with the earlier version of Java (before 1.4). Since then, so called cause facility capabilities were built in the Throwable class.

A throwable contains a snapshot of the execution stack of its thread at the time it was created. It can also contain a message string that gives more information about the error. Finally, it can contain a cause: another throwable that caused this throwable to get thrown. The cause facility is also known as the chained exception facility, as the cause can, itself, have a cause, and so on, leading to a "chain" of exceptions, each caused by another.

A cause can be associated with a throwable in two ways: via a constructor that takes the cause as an argument, or via the initCause(Throwable) method. New throwable classes that wish to allow causes to be associated with them should provide constructors that take a cause and delegate (perhaps indirectly) to one of the Throwable constructors that takes a cause. For example:

Example Code section 6.26: Chaining-aware constructor.
try {
    lowLevelOp();
} catch (LowLevelException le) {
    throw new HighLevelException(le);
}

Because the initCause method is public, it allows a cause to be associated with any throwable, even a "legacy throwable" whose implementation predates the addition of the exception chaining mechanism to Throwable. For example:

Example Code section 6.27: Legacy constructor.
try {
    lowLevelOp();
} catch (LowLevelException le) {
    throw (HighLevelException) new HighLevelException().initCause(le);
}

Further, as of release 1.4, many general purpose Throwable classes (for example Exception, RuntimeException, Error) have been retrofitted with constructors that take a cause. This was not strictly necessary, due to the existence of the initCause method, but it is more convenient and expressive to delegate to a constructor that takes a cause.

By convention, class Throwable and its subclasses have two constructors, one that takes no arguments and one that takes a String argument that can be used to produce a detail message. Further, those subclasses that might likely have a cause associated with them should have two more constructors, one that takes a Throwable (the cause), and one that takes a String (the detail message) and a Throwable (the cause).


Clipboard

To do:
Add some exercises like the ones in Variables