Java Advanced Placement Study Guide:  Extending Classes, Overriding Methods, and Polymorphic Behavior

Sample question:  Can you downcast an object's reference to a subclass of the class from which the object was originally instantiated?

Published: January 1,  2004
By Richard G. Baldwin

Questions

File JavaAP022.htm


Welcome

This is one in a miniseries of tutorial lessons designed to help you learn the essential features of Java object-oriented programming as identified by The College Board.

Purpose

The purpose of this miniseries is to help you study for the Advanced Placement Examinations designed by the College Board.

Once you understand everything in this miniseries, plus the material in the lessons that I published earlier on Java Data Structures, you should understand the Java programming features that the College Board considers essential for the first two semesters of object-oriented programming education at the university level.

Approach

These lessons provide questions, answers, and explanations designed to help you to understand the subset of Java features covered by the Java Advanced Placement Examinations (as of October, 2001).

Please see the first lesson in the miniseries entitled Java Advanced Placement Study Guide: Introduction to the Lessons, Primitive Types, for additional background information.  The previous lesson was entitled Java Advanced Placement Study Guide:  The this Keyword, static final Variables, and Initialization of Instance Variables.

Supplementary material

In addition to the material in these lessons, I recommend that you also study the other lessons in my extensive collection of online Java tutorials, which are designed from a more conventional textbook approach.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at Baldwin's Java Programming Tutorials.

What is Included?

Click here for a preview of the Java programming features covered by this lesson.



1.  What output is produced by the following program?
public class Ap120{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap120

class Worker{
  void doIt(){
    Base myVar = new A();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

2.  What output is produced by the following program?

public class Ap121{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap121

class Worker{
  void doIt(){
    Base myVar = new A();
    ((A)myVar).test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

3.  What output is produced by the following program?

public class Ap122{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap122

class Worker{
  void doIt(){
    Base myVar = new A();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  abstract public void test();
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

4.  What output is produced by the following program?

public class Ap123{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap123

class Worker{
  void doIt(){
    Base myVar = new A();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

abstract class Base{
  abstract public void test();
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

5.  What output is produced by the following program?

public class Ap124{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap124

class Worker{
  void doIt(){
    Base myVar = new Base();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

abstract class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

6.  What output is produced by the following program?

public class Ap125{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap125

class Worker{
  void doIt(){
    Base myVar = new Base();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

7.  What output is produced by the following program?

public class Ap126{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap126

class Worker{
  void doIt(){
    Base myVar = new Base();
    ((A)myVar).test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

8.  What output is produced by the following program?

public class Ap127{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap127

class Worker{
  void doIt(){
    Base myVar = new A();
    ((A)myVar).test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

9.  What output is produced by the following program?

public class Ap128{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap128

class Worker{
  void doIt(){
    Base myVar = new A();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer and Explanation

10.  What output is produced by the following program?

public class Ap129{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class Ap129

class Worker{
  void doIt(){
    Base myVar = new A();
    myVar.test();
    myVar = new B();
    myVar.test();
    System.out.println("");
  }//end doIt()
}// end class Worker

class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

class B extends Base{
  public void test(){
    System.out.print("B ");
  }//end test()
}//end class B

Answer and Explanation



Copyright 2002, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com


What is Included?

According to the subset document: Note that interfaces are not included in this lesson.  They will be included in a subsequent lesson.


Answers and Explanations

Answer 10

D.  A B

Back to Question 10

Explanation 10

Another illustration of simple polymorphic behavior

In this program, two classes named A and B extend the class named Base, each overriding the method named test() to produce different behavior.  (Typically, overridden methods in different classes will produce different behavior, even though they have the same names.)

Behavior appropriate for object on which method is invoked

In other words, the behavior of the method named test(), when invoked on a reference to an object of type A, is different from the behavior of the method named test() when invoked on a reference to an object of type B.

The method definitions

The definitions of the two classes named A and B, along with the two versions of the overridden method named test() are shown in the following fragment.
 
class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

class B extends Base{
  public void test(){
    System.out.print("B ");
  }//end test()
}//end class B

Store a subclass object's reference as a superclass type

The program declares a reference variable of type Base, instantiates a new object of the class named A, and assigns that object's reference to the reference variable of type Base.  Then it invokes the method named test() on that reference as shown in the following fragment.
 
    Base myVar = new A();
    myVar.test();

Polymorphic behavior applies

Simple polymorphic behavior causes the overridden version of the method named test(), defined in the class named A, to be executed.  This causes the letter A followed by a space character to be displayed on the standard output device.

Store another subclass object's reference as superclass type

Then the program instantiates a new object from the class named B, and assigns that object's reference to the same reference variable, overwriting the reference previously stored there. (This causes the object whose reference was previously stored in the reference variable to become eligible for garbage collection in this case.)

Then the program invokes the method named test() on the reference as shown in the following fragment.
 
    myVar = new B();
    myVar.test();

Polymorphic behavior applies again

This time, simple polymorphic behavior causes the overridden version of the method named test(), defined in the class named B, to be executed.  This causes the letter B followed by a space character to be displayed on the standard output device.

Again, what is runtime polymorphic behavior?

With runtime polymorphic behavior, the method selected for execution is based, not on the type of the reference variable holding the reference to the object, but rather on the actual class from which the object was instantiated.

If the method was properly overridden, the behavior exhibited by the execution of the method is appropriate for an object of the class from which the object was instantiated.

Answer 9

D.  A

Back to Question 9

Explanation 9

This is essentially the program from Question 8 with the redundant downcast removed.  This program illustrates simple polymorphic behavior.

Answer 8

D.  A

Back to Question 8

Explanation 8

Compiles and executes successfully

This program compiles and executes successfully causing the version of the method named test(), which is overridden in the class named A to be executed.  That overridden method is shown in the following fragment.
 
class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

So, what is the issue here?

The purpose of this program is to determine if you understand polymorphic behavior and the role of downcasting, as shown in the following fragment.
 
    Base myVar = new A();
    ((A)myVar).test();

This would be a simple case of polymorphic behavior were it not for the downcast shown in the above fragment.

The downcast is redundant

Actually, the downcast was placed there to see if you could determine that it is redundant.  It isn't required, and it has no impact on the behavior of this program.  This program would behave exactly the same if the second statement in the above fragment were replaced with the following statement, which does not contain a downcast.
 
    myVar.test();

You need to know when downcasting is required, when it isn't required, and to make use of that knowledge to downcast appropriately.

Answer 7

B.  Runtime Error

Back to Question 7

Explanation 7

Storing a reference as a superclass type

You can store an object's reference in any reference variable whose declared type is a superclass of the actual class from which the object was instantiated.

May need to downcast later

Later on, when you attempt to make use of that reference, you may need to downcast it.  Whether or not you will need to downcast will depend on what you attempt to do.

In order to invoke a method ...

For example, if you attempt to invoke a method on the reference, but that method is not defined in or inherited into the class of the reference variable, then you will need to downcast the reference in order to invoke the method on that reference.

Class Base defines method named test()

This program defines a class named Base that defines a method named test().

Class A extends Base and overrides test()

The program also defines a class named A that extends Base and overrides the method named test() as shown in the following fragment.
 
class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

A new object of the class Base

The program instantiates a new object of the class Base and stores a reference to that object in a reference variable of type Base, as shown in the following fragment.
 
    Base myVar = new Base();
    ((A)myVar).test();

Could invoke test() directly on the reference

Having done this, the program could invoke the method named test() directly on the reference variable using a statement such as the following, which is not part of this program.
 
    myVar.test();

This statement would cause the version of the method named test() defined in the class named Base to be invoked, causing the word Base to appear on the standard output device.

This downcast is not allowed

However, this program attempts to cause the version of the method named test() defined in the class named A to be invoked, by downcasting the reference to type A before invoking the method named test().  This is shown in the following fragment.
 
    ((A)myVar).test();

A runtime error occurs

This program compiles successfully.  However, the downcast shown above causes the following runtime error to occur under JDK 1.3:

Exception in thread "main" java.lang.ClassCastException: Base
        at Worker.doIt(Ap126.java:22)
        at Ap126.main(Ap126.java:15)

What you can do

You can store an object's reference in a reference variable whose type is a superclass of the class from which the object was originally instantiated.  Later, you can downcast the reference back to the type (class) from which the object was instantiated.

What you cannot do

However, you cannot downcast an object's reference to a subclass of the class from which the object was originally instantiated.

Answer 6

C.  Base

Back to Question 6

Explanation 6

Totally straightforward code

This rather straightforward program instantiates an object of the class named Base and assigns that object's reference to a reference variable of the type Base as shown in the following fragment.  Then it invokes the method named test() on the reference variable.
 
    Base myVar = new Base();
    myVar.test();

Class Base defines the method named test()

The class named Base contains a concrete definition of the method named test() as shown in the following fragment.  This is the method that is invoked by the code shown in the above fragment.
 
class Base{
  public void test(){
    System.out.print("Base ");};
}//end class Base

Class A is just a smokescreen

The fact that the class named A extends the class named Base, and overrides the method named test(), as shown in the following fragment, is of absolutely no consequence in the behavior of this program.  Hopefully you understand why this is so.  If not, then you still have a great deal of studying to do on Java inheritance.
 
class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Answer 5

A.  Compiler Error

Back to Question 5

Explanation 5

Cannot instantiate an abstract class

This program defines an abstract class named Base.  Then it violates one of the rules regarding abstract classes, by attempting to instantiate an object of the abstract class as shown in the following code fragment.
 
    Base myVar = new Base();

The program produces the following compiler error under JDK 1.3:

Ap124.java:19: Base is abstract; cannot be instantiated
    Base myVar = new Base();
 

Answer 4

C.  A

Back to Question 4

Explanation 4

An abstract class with an abstract method

This program illustrates the use of an abstract class containing an abstract method to achieve polymorphic behavior.

The following code fragment shows an abstract class named Base that contains an abstract method named test().
 
abstract class Base{
  abstract public void test();
}//end class Base

Extending abstract class and overriding abstract method

The class named A, shown in the following fragment extends the abstract class named Base and overrides the abstract method named test().
 
class A extends Base{
  public void test(){
    System.out.print("A ");
  }//end test()
}//end class A

Can store a subclass reference as a superclass type

Because the class named A extends the class named Base, a reference to an object instantiated from the class named A can be stored in a reference variable of the declared type Base.  No cast is required in this case.

Polymorphic behavior

Furthermore, because the class named Base contains the method named test(), (as an abstract method), when the method named test() is invoked on a reference to an object of the class named A, stored in a reference variable of type Base, the overridden version of the method as defined in the class named A will actually be invoked.  This is polymorphic behavior.

The important code

The following code fragment shows the instantiation of an object of the class named A and the assignment of that object's reference to a reference variable of type Base.  Then the fragment invokes the method named test() on the reference variable.
 
    Base myVar = new A();
    myVar.test();

This causes the overridden version of the method named test(), shown in the following fragment, to be invoked, which causes the letter A to be displayed on the standard output device.
 
  public void test(){
    System.out.print("A ");
  }//end test()

Answer 3

A.  Compiler Error

Back to Question 3

Explanation 3

Classes can be final or abstract, but not both

A class in Java may be declared final.  A class may also be declared abstract.  A class cannot be declared both final and abstract.

Behavior of final and abstract classes

A class that is declared final cannot be extended.  A class that is declared abstract cannot be instantiated.  Therefore, it must be extended to be useful.

An abstract class is normally intended to be extended.

Methods can be final or abstract, but not both

A method in Java may be declared final.  A method may also be declared abstract.  However, a method cannot be declared both final and abstract.

Behavior of final and abstract methods

A method that is declared final cannot be overridden.  A method that is declared abstract must be overridden to be useful.

An abstract method doesn't have a body.

abstract classes and methods

A class that contains an abstract method must itself be declared abstract.  However, an abstract class is not required to contain abstract methods.

Failed to declare the class abstract

In this program, the class named Base contains an abstract method named test(), but the class is not declared abstract as required.
 
class Base{
  abstract public void test();
}//end class Base

Therefore, the program produces the following compiler error under JDK 1.3:

Ap122.java:24: Base should be declared abstract; it does not define test() in Base
class Base{

Answer 2

C.  A

Back to Question 2

Explanation 2

If you missed this ...

If you missed this question, you didn't pay attention to the explanation for question 1.

Define a method in a subclass

This program defines a subclass named A, which extends a superclass named Base.  A method named test() is defined in the subclass named A but is not defined in any superclass of the class named A.

Store a reference as a superclass type

The program declares a reference variable of the superclass type, and stores a reference to an object of the subclass in that reference variable as shown in the following code fragment.
 
    Base myVar = new A();

Downcast and invoke the method

Then the program invokes the method named test() on the reference stored as the superclass type, as shown in the following fragment.
 
    ((A)myVar).test();

Unlike the program in Question 1, the reference is downcast to the true type of the object before invoking the method named test().  As a result, this program does not produce a compiler error.

Why is the cast required?

As explained in Question 1, it is allowable to store a reference to a subclass object in a variable of a superclass type.  Also, as explained in Question 1, it is not allowable to directly invoke, on that superclass reference, a method of the subclass object that is not defined in or inherited into the superclass.

However, such invocation is allowable if the programmer purposely downcasts the reference to the true type of the object before invoking the method.
 

Answer 1

A.  Compiler Error

Back to Question 1

Explanation 1

Define a method in a subclass

This program defines a subclass named A, which extends a superclass named Base.  A method named test(), is defined in the subclass named A, which is not defined in any superclass of the class named A.

Store a reference as superclass type

The program declares a reference variable of the superclass type, and stores a reference to an object of the subclass in that reference variable as shown in the following code fragment.  Note that no cast is required to store a reference to a subclass object in a reference variable of a superclass type.  The required type conversion happens automatically in this case.
 
    Base myVar = new A();

Invoke a method on the reference

Then the program attempts to invoke the method named test() on the reference stored as the superclass type, as shown in the following fragment.  This produces a compiler error.
 
    myVar.test();

The reason for the error

It is allowable to store a reference to a subclass object in a variable of a superclass type.  However, it is not allowable to directly invoke, (on that superclass reference), a method of the subclass object that is not defined in or inherited into the superclass.

The following error message is produced by JDK 1.3.

Ap120.java:18: cannot resolve symbol
symbol  : method test  ()
location: class Base
    myVar.test();

The solution is ...

Of course, this error can be avoided by casting the reference to type A before invoking the method as shown below:
 
    ((A)myVar).test();



Copyright 2002, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com

-end-