Lab: Experimenting with Polymorphism through Inheritance JMU - Department of Computer Science Help Tools Lab: Experimenting with Polymorphism through Inheritance Instructions: Answer the following questions one at a time. After answering each question, check your answer (by clicking on the check-mark icon if it is available) before proceeding to the next question. Getting Ready: Before going any further, you should: Setup your development environment. Depending on your development environment, create either a directory or a project for this lab. Download the following files: Document.java FormattedDocument.java Driver1.java Driver2.java Driver3.java Driver4.java Driver5.java Driver6.java to an appropriate directory/folder (e.g., the course downloads directory/folder). In most browsers/OSs, the easiest way to do this is by right-clicking/control-clicking on each of the links above and then selecting Save as... or Save link as.... Add the appropriate files you downloaded to the directory/project you created for this lab. .java Files: In most IDEs, .java files (i.e., source files) can just be copied into the project. See the course "Help" page on your IDE for more information. .class and .jar Files: In some IDEs it is easier to use .class files and in others it is easier to use a .jar file that contains the .class files. Hence, you have been provided with both. See the course "Help" page on your IDE for more information. Resources: In some IDEs, resources (e.g., images, data files) need to be in a special directory whereas in others they need to be in the working directory. See the course "Help" page on your IDE for more information. 1. Finding Errors Related to Polymorphism: This part of the lab will help you learn how to find errors related to polymorphism. Familiarize yourself with the Document and FormattedDocument classes. (Note: These classes are different from any earlier versions you may have seen or used.) Compile all of the classes Execute Driver1. What output was generated?
.
This document has 18 words and 2 lines
George is a little monkey, and all monkeys are curious.
But no monkey is as curious as George.
Is the output correct?
.
Yes. Execute Driver2. What output was generated?
.
This document has 18 words and 2 lines
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
What is wrong with the output?
.
The line count is not consistent with the output. Why is the output incorrect? (Hint: Use a debugger or add output statements to the getLineCount() method.)
.
The getLineCount() method uses the text attribute directly rather than calling the getText() method. 2. Review of Accessibility/Visibility: This part of the lab reviews some issues involving accessibility/visibility. Make the attribute text in the Document class private. Compile the Document and FormattedDocument classes (in that order). What compile-time error messages are generated and why?
.
FormattedDocument.java:50: text has private access in Document
tokenizer = new StringTokenizer(text, " ");
^
Fix the problem (without changing the accessibility/visibility of text). (Hint: How can a FormattedDocument object get access to text if it's private?) What did you do to fix the problem?
.
Used the getText() method rather than the text attribute. That is:
tokenizer = new StringTokenizer(super.getText(), " ");
Compile and execute Driver2. (Note: You should not get a run-time exception. If you do, you made a mistake when you answered the previous question that resulted in an infinite recursion - a method calling itself forever.) What output is generated?
.
This document has 18 words and 2 lines
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Is the output correct? If not, what is incorrect?
.
No. The document does have 18 words but it has 7 lines. 3. Polymorphism Basics: This part of the lab will help you gain a better understanding of the basics of polymorphism through inheritance. In Driver2, the getDescription() message is sent to the object named formatted. What code is executed as a result (i.e., what class is the code in)? Why?
.
Since the message is sent to a FormattedDocument object, the FormattedDocument class is searched first. There is no implementation of a getDescription() method so the search moves to the parent Document class. There is such a method in the Document class and it is executed. (In other words, the getDescription() method that is inherited from the Document class is executed.) Continuing with this same example, the getDescription() method sends the message getLineCount() to this. What code is executed as a result (i.e., what class is the code in)? Why?
.
this refers to the object named formatted in Driver2. Hence, the FormattedDocument class is searched first. But, since the FormattedDocument class does not have an explicit getLineCount() method, the parent class is searched (for an inherited method). There is such a method in the Document class so the code in that class is executed. 4. The Complexities of Polymorphism: This part of the lab will help you gain a better understanding of the complexities that arise because of polymorphism. Change the getLineCount() method in the Document to the following: getLineCount.java public int getLineCount()
{
char character;
int count, i;
String temp;
temp = this.getText();
// Initialize the line counter
count = 1;
if (temp.length() == 0) count = 0; // No words means no lines
// Count the number of newline characters
for (i=0; i < temp.length(); i++)
{
character = temp.charAt(i);
if (character == '\n') count = count + 1;
}
return count;
}
Explain the changes.
.
The new version uses the getText() method (and a local variable) rather than the text attribute. In the modified Driver2, the getText() message is sent to the object named formatted. What code is executed as a result (i.e., what class is the code in)? Why?
.
The getText() message is sent to an object in the FormattedDocument class, so that class is searched first. There is an implementation of the getText() method in that class so it is executed. Review: How do we describe the relationship between the getText() method in the FormattedDocument class and the getText() method in the Document class?
.
The getText() message in the FormattedDocument class overrides the getText() method in the Document class. Given what you know about the way polymorphism is resolved in Java, why is the term used in the answer to the previous question appropriate?
.
It is appropriate to say that the method in the derived class overrides the method in the parent class because it is the one that will be executed. Why wouldn't it be appropriate to say that the method in the derived class replaces the method in the base class?
.
Because the implementation in the derived class can call the implementation in the base class. (Indeed, any method in the derived class can call any overridden method in the base class.) In other words, both implementations are available to the derived class. In Driver2, the getDescription() message is sent to the object named formatted. What code is executed as a result (i.e., what class is the code in)?
.
As before, the code in the Document class. Continuing with this same example, the getDescription() method sends the message getLineCount() to this. What code is executed?
.
this refers to the object named formatted in Driver2. Hence, the FormattedDocument class is searched first. But the FormattedDocument class does not have an explicit getLineCount() method. Hence, the parent class is searched (for an inherited method). There is such a method in the Document class so the code in the Document class is executed. Continuing with this example, the new version of getLineCount() sends the getText() message to this. What code is executed as a result? Why?
.
This one is a little tricky. Remember (from the discussion of the answer to the previous question) that the getLineCount() message is sent to the object named formatted in Driver2 which means that this refers to formatted. Hence, the FormattedDocument class is searched first. There is a getText() method in the FormattedDocument class (it overrides the version in the Document class) so the code in the FormattedDocument class is executed. Compile and execute Driver2. What output is generated?
.
This document has 18 words and 7 lines
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Is the output correct?
.
Yes. Compile and execute Driver1. What output is generated?
.
This document has 18 words and 2 lines
George is a little monkey, and all monkeys are curious.
But no monkey is as curious as George.
Is the output correct?
.
Yes. 5. The Complete Class Hierarchy: This part of the lab will help remind you of something you might have forgotten and some of its implications. Open and read Driver5. Compile and execute Driver5. Review: What is printed by Driver5? Why?
.
false will be printed because a and b are references to different Document objects and the == operator just compares the two references. Change the actual parameter of the call to println() to a.getText() == b.getText(). Compile and execute Driver5. Review: What is printed by Driver5? Why?
.
false will be printed because a.getText() and b.getText() return (references to) different String objects and the == operator just compares the two references. Change the actual parameter of the call to println() to a.equals(b). Compile Driver5. Were any compile-time error messages generated?
.
No. The Document class does not have an implementation of an equals() method. Why does the class compile?
.
All classes in Java implicitly extend the Object class which does have an equals() method. So, the Document class inherits that method. Execute Driver5. What is printed by Driver5?
.
false Is this what you expected?
.
No, since the two Document objects contain the same text. Override the equals() method so that a Document object can be correctly compared to another Document object. (Reminder: Don't use an if statement!) What code did you add to the Document class?
.
public boolean equals(Document other)
{
return this.text.equals(other.text);
}
Do you need to add a similar method to the FormattedDocument class?
.
No, FormattedDocument objects inherit the equals() method from the Document class (and it will work correctly). Change the actual parameter of the call to println() to a.toString(). Compile Driver5. Were any compile-time error messages generated?
.
No. The Document class does not have an implementation of a toString() method. Why does the class compile?
.
Again, because all classes in Java implicitly extend the Object class which does have an toString() method. Compile and execute Driver5. What is printed by Driver5?
.
Something like: Document@15db9742 What is this?
.
In a loose sense, it is a reference to the Document object. In fact, it is a hexadecimal representation of the object's hash code. Override the toString() method so that it returns the result of a call to getText(). What code did you add to the Document class?
.
public String toString()
{
return this.getText();
}
Compile and execute Driver5. What is printed by Driver5?
.
George is a little monkey, and all monkeys are curious.
But no monkey is as curious as George.
Modify Driver5 so that a is instantiated as a FormattedDocument (with a width of 20). Compile and execute Driver5. What is printed by Driver5?
.
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
When the toString() message is sent to a, what method is executed? Why?
.
a is a FormattedDocument, a Document, and an Object. When the toString() message is sent to a the search starts in the FormattedDocument class. There is no such method in that class so the Document class is searched next. There is a toString() method in that class so it is executed. When the getText() message is sent to this (inside of the toString() method), what method is executed? Why?
.
this refers to a. So, when the getText() message is sent to this the search starts in the FormattedDocument class. There is such a method in that class so it is executed. 6. Polymorphism, Overloading and Parameters: This part of the lab will help you gain a better understanding of passing polymorphic objects to overloaded methods. Open and read Driver3. What overloaded methods are in Driver3?
.
It contains two print() methods, one that is passed a Document and one that is passed a FormattedDocument. Compile and execute Driver3. What output was generated?
.
A nicely formatted document:
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Which print() method was executed (i.e., what is the signature of the method that was executed)?
.
The one that is passed a FormattedDocument. That is, the method void print(FormattedDocument). Why was that print() method executed?
.
The simple answer: When the compiler got to the call to print() in main(), it looked for a print() method that can be passed a FormattedDocument (because formatted is declared to be a FormattedDocument) and found one. The more complicated answer: See the answer to the last question in this section. In Driver3, comment-out the print() method that is passed a FormattedDocument. Review: Should Driver3 compile? Why or why not?
.
It should compile because a FormattedDocument "is a" Document (i.e., FormattedDocument specializes Document). Compile and execute Driver3. What output was generated?
.
A document:
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Which print() method was executed?
.
The one that is passed a Document. That is, the method with the signature void print(Document). Why was that print() method executed?
.
There is only one print() method. Remove the comments around the commented-out print() method that is passed a FormattedDocument. (That is, make sure Driver3 again has both print() methods.) In Driver3, modify the declaration of formatted so that it is now a Document. What is the declaration now?
.
Document formatted;
Review: Should Driver3 compile? Why or why not?
.
It should compile because a FormattedDocument "is a" Document (i.e., FormattedDocument specializes Document). Execute Driver3. What output was generated?
.
A document:
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Which print() method was executed?
.
The one that is passed a Document. Why was that print() method executed?
.
When the compiler got to the call to print() in main(), it needed to look for a print() method that can be passed the object named formatted. Since formatted is declared to be a Document object it found only one, the one that is passed a Document. Even though, at run-time, formatted will actually be a FormattedDocument object, the compiler can't always know that. So, it uses the declared type. Why is the text formatted?
.
The getText() message is sent to the object named doc at run-time. At run-time, the doc object is a FormattedDocument. So, the FormattedDocument class is searched first. Without making any other changes in Driver3, comment-out the print() method that is passed a Document. Compile Driver3. What error message is generated?
.
Driver3.java:25: print(FormattedDocument) in Driver3 cannot be applied to (Docum
ent)
print(formatted);
^
1 error
Why is this error message generated?
.
When the compiler got to the call to print() in main(), it needed to look for a print() method that can be passed the object named formatted. Since formatted is declared to be a Document object it didn't find one. Open and read Driver4. What overloaded methods are in Driver4?
.
It contains two print() methods, one that is passed a Document and one that is passed a FormattedDocument. Compile and execute Driver4. What output was generated?
.
A document:
George is a little monkey, and all monkeys are curious.
But no monkey is as curious as George.
A document:
George is a little
monkey, and all
monkeys are
curious.
But no
monkey is as curious
as George.
Which print() method was executed in iteration 0?
.
The one that is passed a Document. That is, the method void print(Document). Why was that print() method executed?
.
When the compiler got to the call to print() in main(), it needed to look for a print() method that can be passed the object named documents[0]. Since documents is declared to be a Document[] object, documents[0] is declared to be a Document object. There is only one print() method that is passed a Document object. Why isn't the text formatted?
.
The getText() message is sent to the object named documents[0] at run-time. At run-time, the documents[0] object is a Document. So, the Document class is searched first. Which print() method was executed in iteration 1?
.
The one that is passed a Document. That is, the method void print(Document). Why was that print() method executed?
.
When the compiler got to the call to print() in main(), it needed to look for a print() method that can be passed the object named documents[1]. Since documents is declared to be a Document[] object, documents[1] is declared to be a Document object. There is only one print() method that is passed a Document object. Why is the text formatted?
.
The getText() message is sent to the object named documents[1] at run-time. At run-time, the documents[1] object is a FormattedDocument. So, the FormattedDocument class is searched first. Change the declaration of documents from Document[] to FormattedDocument[] and compile Driver4.java. What error messages were generated? Why?
.
Driver4.java:23: error: incompatible types
documents = new Document[2];
^
required: FormattedDocument[]
found: Document[]
Driver4.java:24: error: incompatible types
documents[0] = new Document(text);
^
required: FormattedDocument
found: Document
2 errors
These error message were generated because a Document is not a FormattedDocument. Now change the declaration of documents back to Document[] and the allocation of memory for documents from Document[] to FormattedDocument[]. Will Driver4.java compile? Hint: Think about one line at a time.
.
It will compile. documents is now declared to be a Document[] array and a FormattedDocument is a Document so the allocation of memory for the array documents will compile. What may be surprising is that the statement that instantiates documents[0] compiles. However, it makes sense that it does. documents[0] is declared to be a Document object and a Document object is assigned to it. So, the compiler doesn't object. Finally, the statement that instantiates documents[1] compiles because documents[1] is declared to be a Document and a FormattedDocument objects (which "is a" Document object) is assigned to it. Execute Driver4. What error message is generated? Why?
.
Exception in thread "main" java.lang.ArrayStoreException: Document
at Driver4.main(Driver4.java:24)
This error is generated because, at run-time, a Document object is being assigned to an element of an array that is actually a (reference to) a FormattedDocument and a Document is not a FormattedDocument. (In Java, arrays are reifiable, which means that information about them are fully available at run-time. This is not true of collections, which has implications for what statements will and won't compile. However, that's an issue that is beyond the scope of this lab.) Open and read Driver6. What overloaded methods are in Driver6?
.
It contains three print() methods, one that is passed an Object, one that is passed a Document and one that is passed a FormattedDocument. Compile and execute Driver6. What output was generated?
.
doc is a FormattedDocument
Which print() method was executed (i.e., what is the signature of the method that was executed)?
.
The one that is passed a FormattedDocument. That is, the method void print(FormattedDocument). Why was that print() method executed?
.
The simple answer: When the compiler got to the call to print() in main(), it looked for a print() method that can be passed a FormattedDocument (because formatted is declared to be a FormattedDocument) and found one. The more complicated answer: See the answer to the last question in this section. Comment-out the print() method that is passed a FormattedDocument. Compile and execute Driver6. What output was generated?
.
doc is a Document
Which print() method was executed (i.e., what is the signature of the method that was executed)?
.
The one that is passed a Document. That is, the method void print(Document). Why was that print() method executed?
.
When the compiler got to the call to print() in main(), it needed to look for a print() method that can be passed the object named formatted. It found two, one that is passed a Document and one that is passed an Object. Both are appropriate because though formatted is declared to be a FormattedDocument it is polymorphic (i.e., it is an Object, a Document, and a FormattedDocument. The compiler chooses the most specialized/specific version which, in this case, is the version that is passed a Document. Department of Computer Science Copyright 2022