Creating a Custom Exception

   

Java™ 2 Primer Plus
By Steven Haines, Steve Potts

Table of Contents
Chapter 9.  Exception Handling


Consider the Car example discussed at the beginning of the chapter. We defined methods in the Car class that have potential error conditions for which we want to check. For example, when you accelerate a car you must ensure that it is currently running, that it is in drive (or in a gear for a manual transmission car), and that it has the appropriate amount of fluids (gas and oil) before increasing the speed. If any of these conditions are not valid, you will want to throw an exception.

Let's first define a custom exception named CarException, as in Listing 9.1.

Listing 9.1 CarException.java
 public class CarException extends Exception {    private boolean engineRunning = true;    private boolean gas = true;    private boolean oil = true;    public CarException() {      super();    }    public CarException( String message ) {      super( message );    }    public void setEngineRunning( boolean engineRunning ) {      this.engineRunning = engineRunning;    }    public boolean isEngineRunning() {      return this.engineRunning;    }    public void setGas( boolean gas ) {      this.gas = gas;     }    public boolean hasGas() {      return this.gas;    }    public void setOil( boolean oil ) {      this.oil = oil;    }    public boolean hasOil() {      return this.oil;    }  } 

The CarException class extends the Exception superclass and provides the settings for three additional parameters: the engine state, the oil state, and the gas state. Thus, a class that throws a car exception can alert the caller to the nature of the problem is the engine turned off, is the car out of oil, or is the car out of gas?

The Car class has been revamped to throw exceptions when errors occur and has been renamed to NewCar, so as not to create any naming conflicts. Listing 9.2 shows the code for the NewCar class.

Listing 9.2 NewCar.java
 public abstract class NewCar  {      // Transmission types      public static final int AUTOMATIC = 0;      public static final int TIPTRONIC = 1;      public static final int MANUAL = 2;      // Attributes      protected String typeOfCar;      protected int horsePower;      protected int maximumSpeed;      protected int numberOfDoors;      protected String paint;      protected int gasCapacity;      protected int oilCapacity;      protected int transmission;      // State attributes      protected boolean running = false;      protected int currentSpeed;      protected int currentGas;      protected int currentOil;      public NewCar( String typeOfCar,                     int horsePower,                     int maximumSpeed,                      int numberOfDoors,                     String paint,                     int gasCapacity,                     int oilCapacity,                     int transmission ) {          this.typeOfCar = typeOfCar;          this.horsePower = horsePower;          this.maximumSpeed = maximumSpeed;          this.numberOfDoors = numberOfDoors;          this.paint = paint;          this.gasCapacity = gasCapacity;          this.oilCapacity = oilCapacity;          this.transmission = transmission;      }      public void start() throws CarException {          if( this.running == true )          {              CarException e = new CarException(                "Car is already running --- nasty grinding noise!" );               e.setEngineRunning( true );              throw e;          }          running = true;       }      public void stop() throws CarException {          if( this.running == false )          {              CarException e = new CarException( "Car is not running!" );              e.setEngineRunning( false );              throw e;          }          running = false;      }      public boolean isRunning() {          return running;      }      public abstract void accelerate() throws CarException;      public abstract void decelerate() throws CarException;      public abstract void tuneUp();      public abstract void changeOil();      public int getCurrentSpeed() {          return currentSpeed;      }      public String toString() {          return typeOfCar;      }  } 

From Listing 9.2 you can see that the start, stop, accelerate, and decelerate methods are each declared to throw the CarException. The start and stop methods are implemented in the NewCar class and handle the logic for throwing the CarException. The accelerate and decelerate methods are both declared to be abstract, but for a subclass to throw an exception from a method it must be declared to be throwable in the superclass. A subclass can remove an exception from the exception list, but it cannot add anything that is not already declared in the superclass. One technique for addressing this issue is to declare that a method can throw the exception or throwable class so that a subclass is free to throw any exception it wants. Although this workaround is functional, it is often bad programming practice because you will want to define specifically what exceptions subclasses of your superclass can throw. Thus, the accelerate and decelerate methods are declared to throw the CarException.

Listing 9.3 shows the code for the modified Porsche class.

Listing 9.3 Porsche.java
 public class Porsche extends NewCar  {      // Attributes      private int turbos = 2;      private boolean nos;      // State attributes      private boolean turbo1Engaged = false;      private boolean turbo2Engaged = false;      private boolean nosEnabled = false;      public Porsche()      {          super( "Porsche",                 450,                 220,                 2,                 "Yellow",                 15,                 5,                 NewCar.TIPTRONIC );      }      public void accelerate() throws CarException      {          // Check to see if we are running or not          if( running == false )          {          CarException e = new CarException(                "Car is not running, cannot accelerate!" );              e.setEngineRunning( false );              throw e;          }          // Create a variable representing how much we are going to          // accelerate this second          int increment = 15;          // Check the turbos; they add 5mph per second acceleration          if( turbo1Engaged )          {              increment += 5;          }          if( turbo2Engaged )          {              increment += 5;           }          // Check the NOS; it represents 15mph per second          if( nos )          {              increment += 15;           }          // Increment the current speed          currentSpeed += increment;          if( currentSpeed > maximumSpeed ) currentSpeed = maximumSpeed;      }      public void decelerate() throws CarException      {          // Check to see if we are running or not          if( running == false )          {          CarException e = new CarException(                "Car is not running, cannot decelerate!" );              e.setEngineRunning( false );              throw e;          }          currentSpeed -= 20;          if( currentSpeed < 0 ) currentSpeed = 0;       }      public void tuneUp()      {          System.out.println( "Tuning up a porsche..." );      }      public void changeOil()      {          System.out.println( "Changing a porsche's oil..." );      }      public void engageTurbos()      {          turbo1Engaged = true;          turbo2Engaged = true;      }      public void disengageTurbos()      {          turbo1Engaged = false;          turbo2Engaged = false;       }      public void engageNOS()      {          nos = true;          maximumSpeed += 50;      }      public void disengageNOS()      {          nos = false;           maximumSpeed -= 50;      }      public String toString()      {          return "A shiny new " + paint + " Porsche!";      }      public static void main( String[] args )      {          Porsche p = new Porsche();          System.out.println( "My new car: " + p );           try          {              //p.start();              System.out.println( "Current speed: " + p.getCurrentSpeed() );              for( int i=0; i<20; i++ )              {                  if( i == 5 )                  {                      p.engageTurbos();                  }                  else if( i == 14 )                  {                      p.engageNOS();                  }                  p.accelerate();                  System.out.println( "Current speed: " + p.getCurrentSpeed() );              }              p.disengageNOS();              p.disengageTurbos();              while( p.getCurrentSpeed() > 0 )              {                  p.decelerate();                  System.out.println( "Current speed: " + p.getCurrentSpeed() );              }              p.stop();          }          catch( CarException ce )          {              ce.printStackTrace();          }      }  } 

From Listing 9.3 you can see that the accelerate and decelerate methods are declared to throw the CarException and, in fact, check to see whether the car is running before performing its function, and if not it throws the CarException. In a more robust implementation it would also check the fluids and throw a CarException if the Porsche was out of gas or oil. Not only do the accelerate and decelerate methods set the text of the exception to describe the problem, they also call the CarException's setEngineRunning method to inform the caller that the engine is not running.

The Porsche's main method executes all the Porsche's methods inside of a try block and prints a stack trace if the CarException is thrown. Initially, the main method does not call the Porsche's start method, causing the subsequent accelerate method to fail and an exception to be thrown. If you uncomment the call to the start method, the output will look similar to that in the previous chapter. However, with the start method commented out, the result should look something similar to the following:

 My new car: A shiny new Yellow Porsche!  Current speed: 0  CarException: Car is not running, cannot accelerate!          at Porsche.accelerate(Porsche.java:29)          at Porsche.main(Porsche.java:129) 

Remember that the stack trace shows you every method that was called in the reverse order that it was called. If you read the CarException backward you'll see that the Porsche's main method called the Porsche's accelerate method, and then the accelerate method threw a CarException with the message body Car is not running, cannot accelerate! Furthermore, if we were to call the CarException's isEngineRunning method it would return false.


       
    Top
     



    Java 2 Primer Plus
    Java 2 Primer Plus
    ISBN: 0672324156
    EAN: 2147483647
    Year: 2001
    Pages: 332

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net