slanted W3C logo

Test 7: Traversal

Getting the Last Element

Portfolio is an indexed collection. Retrieving the last element can be done using:

// pf is a Portfolio

Investment last = pf.get(pf.size() - 1);

Getting the Last Element

GlobalCredit only supports indexed traversal. To retrieve the last element, you must traverse the collection:

// gc is a GlobalCredit

CreditCard last = null;
for (CreditCard cc : gc)
{
   last = cc;
}

Filling to Capacity

Portfolio is statically allocated; thus, it has a finite capacity. Its add method will fail (return false) when the collection is full:

while (pf.add(last))
{
}

Reversing a Portfolio

Portfolio is an indexed collection. To make a reversed copy of a Portfolio, traverse the Portfolio from back to front:

// reverse is the reversed copy of pf

for (int i = pf.size() - 1; i >= 0; i--)
{
   reverse.add(pf.get(i));
}

Test 8

Test 8 will look a lot like Test 7, except you will need to search for an element with some particular characteristic.

To perform search, you must be able to traverse the collection with a loop.

All 4 versions of Test 7 are at http://www.cse.yorku.ca/course_archive/2010-11/F/1020/sectionE/index.shtml

Day 25 — Casting in Class Hierarchies

Recap: Polymorphism

Polymorphism allows a subclass type to appear and behave like its superclass type. For example, a RewardCard can be used exactly like a CreditCard:

CreditCard cc = new RewardCard(123, "Sally Sixpack");

Date issued = cc.getIssueDate();
Date expired = cc.getExpiryDate();
double limit = cc.getLimit();
// and so on, using any CreditCard method

Casting

In Java, you use a cast to convert one type to another type. For example, you can use a cast an integer type to a floating point type when dividing two numbers:

double oneHalf = (double) 1 / 2;

Similarly, it is possible to convert the type of an object reference to another type. For example, you can convert a RewardCard reference to a CreditCard reference:

// declared type : RewardCard
// actual type   : RewardCard
RewardCard rc = new RewardCard(123, "Sally Sixpack");

// ok, RewardCard can substitute for CreditCard
CreditCard cc = (CreditCard) rc;

Upcasting

RewardCard rc = new RewardCard(123, "Sally Sixpack");
CreditCard cc = (CreditCard) rc;

Such a cast is called an upcast because it casts up the inheritance hierarchy.


Upcasting is legal but unnecessary because of substitutability: You can always substitute a child class instance for a parent class instance.

Downcasting

Sometimes, it is necessary to cast down the inheritance hierarchy using a downcast.

// gc is a GlobalCredit
// someCardNumber is the number of a RewardCard

// declared type : CreditCard
// actual type   : RewardCard (we hope!)
CreditCard cc = gc.get(someCardNumber);

// ok, actual type of cc is RewardCard
RewardCard rc = (RewardCard) cc;


Downcasting

Substitutability does not automatically apply when downcasting: a parent class instance CAN NOT substitute for a child class instance.

This means that a downcast can fail:

// declared type : CreditCard
// actual type   : CreditCard
CreditCard cc = new CreditCard(123456, "Sally Sixpack");

// whoops! CreditCard cannot substitute for RewardCard
RewardCard rc = (RewardCard) cc;

This results in a run-time error (why not a compile-time error?).

Downcasting

Suppose you have a collection of credit cards that may or may not include reward cards. When you generate the monthly statement for each card, you want to print the reward points balance for the reward cards.

// gc is a GlobalCredit collection

for (CreditCard cc : gc)
{
   output.print(cc.getNumber());

   // print amount owing
   output.printf("  Balance : $%-8.2f", cc.getBalance());
   
   // print reward points
   RewardCard rc = (RewardCard) cc;
   output.printf("Bonus points : %d%n", rc.getPointBalance());
}

The above code compiles, but fails at runtime with an exception if one or more of the cards in the collection is not a RewardCard.

import java.io.PrintStream;
import java.util.Random;
import type.lib.CreditCard;
import type.lib.RewardCard;
import type.lib.GlobalCredit;

public class PolymorphismFail
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      GlobalCredit gc = new GlobalCredit();
      
      // Randomly populate the GlobalCredit colllection
      // with credit and reward cards.
      final int NUM_CARDS = 10;
      Random rng = new Random();
      for (int i = 1; i <= NUM_CARDS; i++)
      {
         CreditCard cc = null;
         if (rng.nextBoolean())
         {
            cc = new CreditCard(i, "Some Name");
            output.printf("%s CreditCard%n", cc.getNumber());
         }
         else
         {
            cc = new RewardCard(i, "Another Name");
            output.printf("%s RewardCard%n", cc.getNumber());
         }
         gc.add(cc);
      }
      output.println();
      
      for (CreditCard cc : gc)
      {
         output.print(cc.getNumber());
         
         // print amount owing
         output.printf("  Balance : $%-8.2f", cc.getBalance());
         
         // print reward points; the cast will fail if cc is a CreditCard
         RewardCard rc = (RewardCard) cc;
         output.printf("\tBonus points : %d%n", rc.getPointBalance());
      }
   }
}

instanceof

What we need is a way to check if a CreditCard reference refers to an object that is or is not a RewardCard (or something substitutable for a RewardCard). In Java, you use the instanceof operator to check if a reference points to an object of a specified type:

CreditCard cc1 = new CreditCard(123, "Sally Sixpack");
CreditCard cc2 = new RewardCard(123, "Sally Sixpack");

// does cc1 point to a CreditCard?
boolean isCreditCard = cc1 instanceof CreditCard;
output.println(isCreditCard);

// does cc2 point to a CreditCard?
isCreditCard = cc2 instanceof CreditCard;
output.println(isCreditCard);

// does cc1 point to a RewardCard?
boolean isRewardCard = cc1 instanceof RewardCard;
output.println(isRewardCard);

// does cc2 point to a RewardCard?
isRewardCard = cc2 instanceof RewardCard;
output.println(isRewardCard);

The above code fragment prints:

true
true
false
true

x instanceof Y returns true if the reference variable x points to an object that is an instance of class Y or a subclass of Y.

instanceof

Note that the compiler only allows the client to use instanceof where the operation is sensible. It is a compile-time error if the reference has a type that is not in the same branch of the inheritance hierarchy as the type name:

// all of these examples are compile-time errors
boolean isInstanceOf;

Date d = new Date();
isInstanceOf = d instanceof String;

Fraction f = new Fraction();
isInstanceOf = f instanceof CreditCard;

Scanner s = new Scanner(System.in);
isInstanceOf = s instanceof Integer;

Downcasting and instanceof

If you need to downcast, you should use instanceof to prevent runtime errors.

// assume gc is a GlobalCredit collection

for (CreditCard cc : gc)
{
   output.print(cc.getNumber());

   // print amount owing
   output.printf("  Balance : $%.2f%n", cc.getBalance());
   
   // print reward points if cc is a RewardCard
   if (cc instanceof RewardCard)
   {
      RewardCard rc = (RewardCard) cc;
      output.printf("Bonus points : %d%n", rc.getPointBalance());
   }
}