JMU CS239 - Advanced Programming
Help Policies Solutions Study-Aids Syllabus 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.

You may work on this assignment alone or in a group.

You must be checked-off at the end of the lab period. Otherwise, you must submit a hardcopy of the worksheet before 5:00PM today.

Getting Ready: Before going any further, you should:

  1. Setup your development environment.
  2. Depending on your development environment, create either a directory or a project for this lab.
  3. Download the following files:
    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....
  4. 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.
  1. Familiarize yourself with the Document and FormattedDocument classes. (Note: These classes are different from any earlier versions you may have seen or used.)
  2. Compile all of the classes
  3. Execute Driver1.
  4. 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.
    
    Expand
  5. Is the output correct?


    Yes.
    Expand
  6. Execute Driver2.
  7. 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.
    
    Expand
  8. What is wrong with the output?


    The line count is not consistent with the output.
    Expand
  9. 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.
    Expand
2. Review of Accessibility/Visibility: This part of the lab reviews some issues involving accessibility/visibility.
  1. Make the attribute text in the Document class private.
  2. Compile the Document and FormattedDocument classes (in that order).
  3. What compile-time error messages are generated and why?


    FormattedDocument.java:50: text has private access in Document
            tokenizer = new StringTokenizer(text, " ");
                                            ^
    
    Expand
  4. Fix the problem (without changing the accessibility/visibility of text). (Hint: How can a FormattedDocument object get access to text if it's private?)
  5. What did you do to fix the problem?


    Used the getText() method rather than the text attribute. That is:
    	tokenizer = new StringTokenizer(super.getText(), " ");
    
    Expand
  6. 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.)
  7. 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.
    
    Expand
  8. Is the output correct? If not, what is incorrect?


    No. The document does have 18 words but it has 7 lines.
    Expand
3. Polymorphism Basics: This part of the lab will help you gain a better understanding of the basics of polymorphism through inheritance.
  1. 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.)
    Expand
  2. 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.
    Expand
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.
  1. 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;
    }
            
  2. Explain the changes.


    The new version uses the getText() method (and a local variable) rather than the text attribute.
    Expand
  3. 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.
    Expand
  4. 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.
    Expand
  5. 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.
    Expand
  6. 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.
    Expand
  7. 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.
    Expand
  8. 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.
    Expand
  9. 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.
    Expand
  10. Compile and execute Driver2.
  11. 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.
    
    Expand
  12. Is the output correct?


    Yes.
    Expand
  13. Compile and execute Driver1.
  14. 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.
    
    Expand
  15. Is the output correct?


    Yes.
    Expand
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.
  1. Open and read Driver5.
  2. Compile and execute Driver5.
  3. 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.
    Expand
  4. Change the actual parameter of the call to println() to a.getText() == b.getText().
  5. Compile and execute Driver5.
  6. 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.
    Expand
  7. Change the actual parameter of the call to println() to a.equals(b).
  8. Compile Driver5.
  9. Were any compile-time error messages generated?


    No.
    Expand
  10. 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.
    Expand
  11. Execute Driver5.
  12. What is printed by Driver5?


    false
    Expand
  13. Is this what you expected?


    No, since the two Document objects contain the same text.
    Expand
  14. Override the equals() method so that a Document object can be correctly compared to another Document object. (Reminder: Don't use an if statement!)
  15. What code did you add to the Document class?


        public boolean equals(Document other)
        {
            return this.text.equals(other.text);
        }
    
    Expand
  16. 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).
    Expand
  17. Change the actual parameter of the call to println() to a.toString().
  18. Compile Driver5.
  19. Were any compile-time error messages generated?


    No.
    Expand
  20. 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.
    Expand
  21. Compile and execute Driver5.
  22. What is printed by Driver5?


    Something like:
    Document@15db9742
    Expand
  23. 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.
    Expand
  24. Override the toString() method so that it returns the result of a call to getText().
  25. What code did you add to the Document class?


        public String toString()
        {
            return this.getText();
        }
    
    Expand
  26. Compile and execute Driver5.
  27. What is printed by Driver5?


    George is a little monkey, and all monkeys are curious.
    But no monkey is as curious as George.
    
    Expand
  28. Modify Driver5 so that a is instantiated as a FormattedDocument (with a width of 20).
  29. Compile and execute Driver5.
  30. What is printed by Driver5?


    George is a little
    monkey, and all
    monkeys are
    curious.
    But no
    monkey is as curious
    as George.
    
    Expand
  31. 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.
    Expand
  32. 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.
    Expand
6. Polymorphism, Overloading and Parameters: This part of the lab will help you gain a better understanding of passing polymorphic objects to overloaded methods.
  1. Open and read Driver3.
  2. What overloaded methods are in Driver3?


    It contains two print() methods, one that is passed a Document and one that is passed a FormattedDocument.
    Expand
  3. Compile and execute Driver3.
  4. 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.
    
    Expand
  5. 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).
    Expand
  6. 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.

    Expand
  7. In Driver3, comment-out the print() method that is passed a FormattedDocument.
  8. Review: Should Driver3 compile? Why or why not?


    It should compile because a FormattedDocument "is a" Document (i.e., FormattedDocument specializes Document).
    Expand
  9. Compile and execute Driver3.
  10. What output was generated?


    A document:
    
    George is a little
    monkey, and all
    monkeys are
    curious.
    But no
    monkey is as curious
    as George.
    
    Expand
  11. Which print() method was executed?


    The one that is passed a Document. That is, the method with the signature void print(Document).
    Expand
  12. Why was that print() method executed?


    There is only one print() method.
    Expand
  13. Remove the comments around the commented-out print() method that is passed a FormattedDocument. (That is, make sure Driver3 again has both print() methods.)
  14. In Driver3, modify the declaration of formatted so that it is now a Document.
  15. What is the declaration now?


    Document        formatted;
    
    Expand
  16. Review: Should Driver3 compile? Why or why not?


    It should compile because a FormattedDocument "is a" Document (i.e., FormattedDocument specializes Document).
    Expand
  17. Execute Driver3.
  18. What output was generated?


    A document:
    
    George is a little
    monkey, and all
    monkeys are
    curious.
    But no
    monkey is as curious
    as George.
    
    Expand
  19. Which print() method was executed?


    The one that is passed a Document.
    Expand
  20. 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.

    Expand
  21. 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.
    Expand
  22. Without making any other changes in Driver3, comment-out the print() method that is passed a Document.
  23. Compile Driver3.
  24. What error message is generated?


    Driver3.java:25: print(FormattedDocument) in Driver3 cannot be applied to (Docum
    ent)
            print(formatted);
            ^
    1 error
    
    Expand
  25. 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.
    Expand
  26. Open and read Driver4.
  27. What overloaded methods are in Driver4?


    It contains two print() methods, one that is passed a Document and one that is passed a FormattedDocument.
    Expand
  28. Compile and execute Driver4.
  29. 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.
    
    Expand
  30. Which print() method was executed in iteration 0?


    The one that is passed a Document. That is, the method void print(Document).
    Expand
  31. 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.
    Expand
  32. 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.
    Expand
  33. Which print() method was executed in iteration 1?


    The one that is passed a Document. That is, the method void print(Document).
    Expand
  34. 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.
    Expand
  35. 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.
    Expand
  36. 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.

    Expand
  37. 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.

    Expand
  38. 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.)

    Expand
  39. Open and read Driver6.
  40. 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.
    Expand
  41. Compile and execute Driver6.
  42. What output was generated?


    doc is a FormattedDocument
    
    Expand
  43. 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).
    Expand
  44. 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.

    Expand
  45. Comment-out the print() method that is passed a FormattedDocument.
  46. Compile and execute Driver6.
  47. What output was generated?


    doc is a Document
    
    Expand
  48. 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).
    Expand
  49. 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.
    Expand

Copyright 2022