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