Personal tools
You are here: Home Information Systems Java Platform Introduction Class - subclass Interactions

Class - subclass Interactions

Document Actions
  • Send this
  • Print this
  • Content View
  • Bookmarks

Class - subclass Interactions

<-- Previous

Table of Contents

Next -->

Overriding a method of the superclass

When we build a subclass, we can include in it a method that has the same name as a method in the superclass, and which is usually intended to do the same thing for the subclass as it does for the superclass, the difference being that the new method should have a different implementation. For example, let us assume that in the case of a savings account the bank charges us a fee of R5.00 for making a cash deposit larger than R1000.00. This means that making a deposit to a savings account is slightly different from making a deposit to an ordinary bank account. We therefore implement a method deposit(double amount) in the class SavingsAccount which allows for this difference. The method deposit() in the subclass overrides the method of the same name in the superclass, i.e. the superclass method is ignored and the subclass method is used. Note that in order to override a method of a superclass, the subclass must define a method of the same name and the same parameters:

//superclass method
public void aMethod(int aNumber){
	...
}

//subclass method
public void aMethod(double aNumber){
/*does not override the superclass method because the 
parameter of this method is different from that of the 
superclass method of the same name. */
	...
}

//subclass method
public void aMethod(int aNumber){
/*overrides the superclass method of the same name
and identical parameter.*/
	...
}

In overriding the BankAccount method deposit(double amount) in the SavingsAccount subclass there is a pitfall however. Considering how we have called the getBalance() method in the getInterest() method of SavingsAccount, we may be tempted to write the overriding method deposit in SavingsAccount like this:

public void deposit(double amount){   
   /*deposit the amount minus fee in the 
   savings account*/
   if(amount > 1000.00){
      this.deposit(amount - 5.0);  //ERROR!
   } else {
      this.deposit(amount);  //ERROR!
   }
}

In the code above we have just written an endless loop - the deposit() method calls itself in the if ... else construct, runs again and then calls itself again in the if ... else construct, and so on until the application dies in the endless circle. To prevent this, we must either change the name of the subclass method (in which case we are no longer overriding a superclass method), or we must use the Java syntax designed to allow us to specifically call the superclass method of the same name from inside the subclass method. We don't want to change the name of the method because this produces redundancy - we already have a method that makes deposits. Rather, we'll use the special Java syntax:

public void deposit(double amount){   
   /*deposit the amount minus fee in the 
   savings account*/
   if(amount > 1000.00){
      //Correct superclass method call
      super.deposit(amount - 5.0);  
   } else {
      super.deposit(amount);
   }
}

The actual error we made was to forget that the keyword this refers to the current object, i.e. an object of class SavingsAccount - what we really needed was to refer to a BankAccount method to make the final deposit after the fee had been subtracted. We have to use the BankAccount method because SavingsAccount has no access to balance; balance is a private variable of the BankAccount class.

This might seem confusing because previously we used this.deposit(initialBalance) in the constructor; why was this not a problem? The difference is that in the original version we did not override the deposit() method; having done this override we now need to understand the difference between making calls to the version of deposit() that is a member of SavingsAccount and deposit() that is a member of BankAccount. We don't need to change the constructor for SavingsAccount because this.deposit() calls the deposit method of SavingsAccount, which method now correctly uses the deposit method of BankAccount to initialise the BankAccount instance variable balance.

This whole situation is a product of the rule that instance methods can only be called on an instance of the class to which they belong. Even though deposit() is orginally a member of SavingsAccount by inheritance, it modifies the private variable balance of the BankAccount class; this means that SavingsAccount cannot directly modify balance. Overriding deposit() means that we now have to be very careful to make sure that we are making the correct method calls for the result we want.

Here is the modified code for SavingsAccount.

 

 

Using a superclass constructor

There is a way in which we can elegantly solve some of the tricky bits in subclassing. The constructors of a class are public, and so our subclass has access to them. We can call the superclass constructor as the first line of the subclass constructor. In the case of SavingsAccount a call to the superclass constructor will automatically intialise all the instance variables of BankAccount, because this is what the function of the BankAccount constructors is. To use a superclass constructor we simply use:
super(parametersOfSuperclassConstructor)

public SavingsAccount(double rate, double initialBalance){
    super(initialBalance);  //invokes a constructor of BankAccount
    interestRate = rate;
}

Now balance is automatically initialised to initialBalance. This does not affect the overriding method deposit() - it must still make correct calls to super.deposit() when further deposits are made to the savings account. 

Testing SavingsAccount

We need to test our subclass to make sure that we've done all the right things in the subclassing process. We need to check that the balance is being stored correctly, and that the subclass methods are working. The easiest way to do this is to create a new SavingsAccount and then call the getInterest() method (this method will only work properly if the constructor did its job correctly).

Download the two Java source code files for BankAccount.java and SavingsAccount.java by left-clicking their links, and compile them.

The class SavAccTest does the testing job for us:

Take a careful look at this class, and notice the transformation that we've done in order to return a value for balance that extends to only 3 decimal places. This has been done to do away with the long string of numerals after the decimal point that usually makes up a number of type double. Java does have methods that format numbers, but we haven't yet made their acquaintance so we're going to use our own (imprecise) technique (we'll deal with formatting numbers properly towards the end of this tutorial).

  1. First we convert the number to a string - look up java.lang.Double.toString() in the Java documentation to see this method of the Double object.
  2. Next we locate the position in the numeral string, of the decimal point.
  3. Finally we return a substring of the numeral string, that stretches from index 0 to periodPos + 4. To see why we use 4 and not 3, look up Java.lang.String.substring() in the Java documentation. We want to return 3 places after the decimal point because we are not rounding off the balance correctly to 2 decimal places. The user can see the third decimal place this way, and simply do the rounding off herself.

    String.substring() may contain a surprise for you - it throws an IndexOutOfBounds exception. This means that if the double number you are trying to parse only has 1 or 2 numerals after the decimal point, your code will return an error. Modify SavAccTest.java so that this problem will not occur.

Now adjust SavAccTest.java so that it will also make sure that R5.00 is deducted for deposits larger than R1000.00.

The SavingsAccount.deposit() method is working correctly. 

Using Javadoc with subclasses

If we subclass one of our classes, we can use Javadoc to build documentation that includes the subclasses. Simply place all the source code files (in this case BankAccount.java and SavingsAccount.java) into a folder, and then run Javadoc with a wildcard character that tells it to document all the files in the folder:

javadoc *.java

This will produce a single set of documents that includes the superclass and its subclasses. The documents for our BankAccount class illustrate this. Notice that javadoc correctly determines the subclasses of the superclass, and also correctly determines which methods are being overriden. Also, do you see that the deposit() method in SavingsAccount is not shown as being inherited from BankAccount - which is correct, because SavingsAccount.deposit() overrides BankAccount.deposit().

<-- Previous

Table of Contents

Next -->

© G. Hearn, & University of the Western Cape, 2006

Copyright 2007-2008, by the Contributing Authors. Cite/attribute Resource. Class - subclass Interactions. (2008, July 16). Retrieved May 24, 2013, from UWC Free Courseware Web site: http://free.uwc.ac.za/freecourseware/information-systems/java-platform-introduction/class-subclass-interactions. This work is licensed under a Creative Commons License : Attribution-ShareAlike 3.0. Creative Commons License : Attribution-ShareAlike 3.0