|
Polymorphism through Inheritance
With Examples in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
public class Chirp {...}
public class ExpandedChirp
extends Chirp {...}
public void addChirp(Chirp ch) {...}
Chirp object to the
addChirp() method?ExpandedChirp object
to the addChirp() method?ch
Chirp object to the
addChirp() method (since the
formal parameter is declared to be a Chirp)ExpandedChirp object to the
addChirp() method (since
the ExpandedChirp class inherits the accessible
attributes from the Chirp class)public class Chirp {...}
public class ExpandedChirp
extends Chirp {...}
Chirp c;
c = new Chirp("JMU");
Chirp c;
c = new ExpandedChirp("JMU");
ExpandedChirp e;
e = new ExpandedChirp("JMU");
ExpandedChirp e;
e = new Chirp("JMU");
Chirp has a getText() methodExpandedChirp overrides the
getText() methodaddChirp() is passed a Chirp
object and calls the getText()
method?addChirp() is passed an
ExpandedChirp
object and calls the getText()
method?
import java.util.*;
/**
* A missive in an instant messaging system
*
* @author Prof. David Bernstein
* @version 2.0
*/
public class Chirp
{
protected String delimiters; // Delimiters between words
protected String text; // The text of the message
/**
* Explicit Value Constructor
*
* @param typed What was typed by the user
*/
public Chirp(String typed)
{
text = typed;
delimiters = " ,.;!?-\n\r";
}
/**
* Get the number of words in this Chirp
*
* @return The number of words
*/
public int getNumberOfWords()
{
int numberOfWords;
StringTokenizer tokenizer;
tokenizer = new StringTokenizer(text, delimiters);
numberOfWords = tokenizer.countTokens();
return numberOfWords;
}
/**
* Get the text of this Chirp
*
* @return The text
*/
protected String getText()
{
return text;
}
}
import java.io.*;
import java.util.*;
/**
* A missive in an instant messaging system
*
* Unlike a normal Chirp, an ExpandedChirp does not
* contain abbreviations -- they are all expanded
*
* @author Prof. David Bernstein
* @version 2.0
*/
public class ExpandedChirp extends Chirp
{
private Properties abbreviations;
/**
* Explicit Value Constructor
*
* @param typed What was typed by the user
*/
public ExpandedChirp(String typed)
{
super(typed);
InputStream is;
abbreviations = new Properties();
try
{
is = new FileInputStream("abbreviations.txt");
abbreviations.load(is);
}
catch (IOException ioe)
{
// There was a problem opening the file
// containing the abbreviations
}
}
/**
* Get the text of this Chirp with all of the
* abbreviations expanded
*
* @return The text
*/
public String getText()
{
String tempText;
// Use the protected attribute in the parent
tempText = replaceAbbreviations(text);
return tempText;
}
/**
* Replace the abbreviations in a message
*
* @param msg The original message
*/
private String replaceAbbreviations(String msg)
{
String replaced, token, word;
StringTokenizer tokenizer;
replaced = "";
// Use the protected attribute in the parent
tokenizer = new StringTokenizer(msg, delimiters, true);
while (tokenizer.hasMoreTokens())
{
token = tokenizer.nextToken();
word = abbreviations.getProperty(token);
if (word == null) replaced += token;
else replaced += word;
}
return replaced;
}
}
Since an ExpandedChirp "is a" Chirp, we
know that a Chirp[] (i.e., an array of references
to Chirp objects) can contain both.
/**
* A missive recorder in an instant messaging system.
* In this version, there is a limit on the total number
* of Chirp objects that can be recorded and all output is
* sent to the console.
*
* @author Prof. David Bernstein
* @version 2.0
*/
public class ChirpRecorder
{
private int currentNumber;
private Chirp[] chirps;
/**
* Explicit Value Constructor
*
* @param size The maximum number of Chirp objects to be recorded
*/
public ChirpRecorder(int size)
{
chirps = new Chirp[size];
currentNumber = 0;
}
/**
* Add a Chirp to this ChirpRecorder (if it is not
* already full)
*
* @param msg The Chirp to record
*/
public void addChirp(Chirp msg)
{
if (currentNumber < chirps.length) {
chirps[currentNumber] = msg;
currentNumber++;
}
}
/**
* Show (on System.out) a previously recorded Chirp
*
* @param number The number of the Chirp to show
*/
public void showChirp(int number)
{
String text;
if (number < currentNumber)
{
text = chirps[number].getText();
System.out.println(text);
}
else
{
System.out.println("No such chirp!");
}
}
}
What we need to know is what happens when an
element's getText() method is called.
/**
* A driver for an instant messaging system.
*
* @author Prof. David Bernstein
* @version 2.0
*/
public class Driver
{
/**
* The entry point of the application
*
* @param args The command line arguments
*/
public static void main(String[] args)
{
ExpandedChirp english;
Chirp message;
ChirpRecorder recorder;
recorder = new ChirpRecorder(10);
message = new Chirp("FWIW I think he's arrogant. G2G");
recorder.addChirp(message);
english = new ExpandedChirp("SLAP. TTYL");
recorder.addChirp(english);
System.out.println("\n\n");
System.out.println("Showing 0 (a Chirp):\n");
recorder.showChirp(0);
System.out.println("\n\n");
System.out.println("\nShowing 1 (an ExpandedChirp):\n");
recorder.showChirp(1);
}
}
What Memory Might Look Like Before The Calls
To recorder.showChirp()
getText() method
of the Chirp object is called
getText()
message is sent to the Chirp
object/**
* A simple "bank" Account
*
* @version 1.0
* @author Prof. David Bernstein, James Madison University
*/
public class Account
{
protected double balance;
protected int id;
/**
* Default constructor
*/
public Account()
{
balance = 0;
id = -1;
}
/**
* Explicit Value Constructor
*
* @param idNumber The ID for the Account
* @param initialDeposit The initial deposit
*/
public Account(int idNumber, double initialDeposit)
{
balance = 0;
id = idNumber;
if (initialDeposit > 0) balance = initialDeposit;
}
/**
* Determine the maximum amount that can be withdrawn
*
* @return The size of the maximum possible withdrawal
*/
public double amountAvailable()
{
return balance;
}
/**
* Deposit money in this Account
*
* @param amount The size of the deposit
*
* @return false if unable to deposit and true otherwise
*/
public boolean deposit(double amount)
{
boolean ok;
ok = false;
if (amount >= 0) {
balance += amount;
ok = true;
}
return ok;
}
/**
* Obtain the ID of this Account
*
* @return The ID of this Account
*/
public int getID()
{
return id;
}
/**
* Withdraw money from this Account
*
* @param amount The size of the withdrawal
*
* @return false if unable to withdraw and true otherwise
*/
public boolean withdraw(double amount)
{
boolean ok;
ok = false;
if (amount >= 0) {
// Check if available funds are sufficient
//
// Note: amountAvailble() is overriden in the
// derived class OverdraftAccount.
// Which version of amountAvailable() will be
// called?
//
if (amountAvailable() >= amount) {
balance -= amount;
ok = true;
}
}
return ok;
}
}
/**
* A "bank" Account that allows the owner to withdraw more money
* than is in the account
*
* @version 1.0
* @author Prof. David Bernstein, James Madison University
*/
public class OverdraftAccount extends Account
{
protected double overdraftLimit;
/**
* Constructor
*
* @param idNumber The ID for the Account
* @param initialDeposit The initial deposit
* @param limit The overdraft limit
*/
public OverdraftAccount(int idNumber,
double initialDeposit, double limit)
{
balance = 0.0;
id = idNumber;
if (initialDeposit > 0) balance = initialDeposit;
overdraftLimit = 0.0;
if (limit > 0.0) overdraftLimit = limit;
}
/**
* Determine the maximum amount that can be withdrawn
*
* Note: This method overrides amountAvailable() in Account.
* This method is called by the method withdraw() which
* is defined in Account.
*
* @return The size of the maximum possible withdrawal
*/
public double amountAvailable() {
return (balance+overdraftLimit);
}
}
/**
* An example that uses an OverdraftAccount object
*
* @version 1.0
* @author Prof. David Bernstein, James Madison University
*/
public class Driver
{
/**
* The entry point of the application
*
* @param args The command line arguments
*/
public static void main(String[] args)
{
double amount;
boolean ok;
OverdraftAccount billSmith;
billSmith = new OverdraftAccount(1001, 10000.00,5000.00);
System.out.println("Account "+billSmith.getID()+
": Opened with "+
"maximum withdrawl of "+
billSmith.amountAvailable());
amount = 5000.00;
ok = billSmith.deposit(amount);
if (ok)
{
System.out.println("Account "+billSmith.getID()+
": Deposit of "+
amount);
}
else
{
System.out.println("Account "+billSmith.getID()+
": Unable to "+
"make deposit of "+amount);
}
amount = 19000.00;
ok = billSmith.withdraw(amount);
if (ok)
{
System.out.println("Account "+billSmith.getID()+
": Withdrawal of "+amount);
}
else
{
System.out.println("Account "+billSmith.getID()+
": Unable to "+
"make withdrawal of "+amount);
}
}
}
What Memory Might Look Like After Constructing The
OverdraftAccount
What Memory Might Look Like After The Deposit
What Memory Might Look Like After The Withdrawal
Chirp Class:
getNumberOfWords() method uses the
protected String attribute
named text
getText() method insteadgetNumberOfWords() message is sent to
a Chirp() object?getNumberOfWords() message is sent to
an ExpandedChirp() object?public class Chirp {...}
public class ExpandedChirp
extends Chirp {...}
public void add(Chirp ch) {...}
public void add(ExpandedChirp ch) {...}
add() is passed a Chirp
that is declared to be a Chirp?add(Chirp ch) is executed -- there is no other
option
add() is passed an ExpandedChirp
that is declared to be an ExpandedChirp?add(ExpandedChirp ch) is executed --
this is not a situation in which dynamic/late binding
is used at run-time, the declared type is used at compile-time
to determine which method will be executed
add() is passed an ExpandedChirp
that is declared to be a Chirp?add(Chirp ch) is executed --
again, the declared type is used at compile-time to determine
which method is executed
public class Chirp {...}
public class ExpandedChirp
extends Chirp {...}
public void add(Chirp ch) {...}
add() is passed an ExpandedChirp
that is declared to be an ExpandedChirp?add() compiles because
an ExpandedChirp is a Chirp
add(Chirp ch) is executed --
there is no other option
add() is passed an ExpandedChirp
that is declared to be a Chirp?add() compiles because
the actual parameter is declared to be the same type as
the formal parameteradd(Chirp ch) is executed --
there is no other option
public class Animal {...}
public class Mammal extends Animal {...}
public class Sheep extends Mammal {...}
public void add(Animal item) {...}
public void add(Mammal item) {...}
add() is passed an Animal
that is declared to be an Animal?add(Animal item) is executed -- there is
no other option
add() is passed a Mammal
that is declared to be a Mammal?add(Mammal item) is executed --
the declared type is used
add() is passed a Sheep
that is declared to be a Sheep?add(Mammal item) is executed --
there is no method matching the declared type but a
Sheep "is a" Mammal and a
Sheep "is a" Animal so
both methods could be executed; the most
specific/specialized version is executed