Certification Objective Polymorphism (Exam Objective 5.2)


Certification Objective —Polymorphism (Exam Objective 5.2)

5.2 Given a scenario, develop code that demonstrates the use of polymorphism. Further, determine when casting will be necessary and recognize compiler vs. runtime errors related to object reference casting.

Remember, any Java object that can pass more than one IS-A test can be considered polymorphic. Other than objects of type object, all Java objects are polymorphic in that they pass the IS-A test for their own type and for class Object.

Remember that the only way to access an object is through a reference variable, and there are a few key things to remember about references:

  • A reference variable can be of only one type, and once declared, that type can never be changed (although the object it references can change).

  • A reference is a variable, so it can be reassigned to other objects, (unless the reference is declared final).

  • A reference variable's type determines the methods that can be invoked on the object the variable is referencing.

  • A reference variable can refer to any object of the same type as the declared reference, or—this is the big one—it can refer to any subtype of the declared type!

  • A reference variable can be declared as a class type or an interface type. If the variable is declared as an interface type, it can reference any object of any class that implements the interface.

Earlier we created a GameShape class that was extended by two other classes, PlayerPiece and Ti1ePiece. Now imagine you want to animate some of the shapes on the game board. But not all shapes can be animatable, so what do you do with class inheritance?

Could we create a class with an animate() method, and have only some of the GameShape subclasses inherit from that class? If we can, then we could have PlayerPiece, for example, extend both the GameShape class and Animatable class, while the TilePiece would extend only GameShape. But no, this won't work! Java supports only single inheritance! That means a class can have only one immediate superclass. In other words, if PlayerPiece is a class, there is no way to say something like this:

 class PlayerPiece extends GameShape, Animatable { // NO!   // more code } 

A class cannot extend more than one class. That means one parent per class. A class can have multiple ancestors, however, since class B could extend class A, and class C could extend class B, and so on. So any given class might have multiple classes up its inheritance tree, but that's not the same as saying a class directly extends two classes.

On the Job 

Some languages (like C++) allow a class to extend more than one other class. This capability is known as "multiple inheritance." The reason that Java's creators chose not to allow multiple inheritance is that it can become quite messy. In a nutshell, the problem is that if a class extended two other classes, and both superclasses had, say, a doStuff() method, which version of doStuff() would the subclass inherit?This issue can lead to a scenario known as the "Deadly Diamond of Death," because of the shape of the class diagram that can be created in a multiple inheritance design. The diamond is formed when classes B and C both extend A, and both B and C inherit a method from A. If class D extends both B and C, and both B and C have overridden the method in A, class D has, in theory, inherited two different implementations of the same method. Drawn as a class diagram, the shape of the four classes looks like a diamond.

So if that doesn't work, what else could you do? You could simply put the animate() code in Gameshape, and then disable the method in classes that can't be animated. But that's a bad design choice for many reasons, including it's more error-prone, it makes the Gameshape class less cohesive (more on cohesion in a minute), and it means the Gameshape API "advertises" that all shapes can be animated, when in fact that's not true since only some of the Gameshape subclasses will be able to successfully run the animate() method.

So what else could you do? You already know the answer—create an Animatable interface, and have only the Gameshape subclasses that can be animated implement that interface. Here's the interface:

 public interface Animatable {    public void animate(); } 

And here's the modified PlayerPiece class that implements the interface:

 class PlayerPiece extends GameShape implements Animatable {    public void movePiece()      System.out.println("moving game piece");    }    public void animate() {      System.out.println("animating...");    }    // more code } 

So now we have a PlayerPiece that passes the IS-A test for both the GameShape class and the Animatable interface. That means a PlayerPiece can be treated polymorphically as one of four things at any given time, depending on the declared type of the reference variable:

  • An object (since any object inherits from Object)

  • A GameShape (since PlayerPiece extends GameShape)

  • A PlayerPiece (since that's what it really is)

  • An Animatable (since PlayerPiece implements Animatable)

The following are all legal declarations. Look closely:

 PlayerPiece player = new PlayerPiece();       Object o = player;       GameShape shape = player;       Animatable mover = player; 

There's only one object here—an instance of type PlayerPiece—but there are four different types of reference variables, all referring to that one object on the heap. Pop quiz: which of the preceding reference variables can invoke the display() method? Hint: only two of the four declarations can be used to invoke the display() method.

Remember that method invocations allowed by the compiler are based solely on the declared type of the reference, regardless of the object type. So looking at the four reference types again—Object, GameShape, PlayerPiece, and Animatable—which of these four types know about the display() method?

You guessed it—both the GameShape class and the PlayerPiece class are known (by the compiler) to have a display() method, so either of those reference types can be used to invoke display(). Remember that to the compiler, a PlayerPiece IS-A Gameshape, so the compiler says, "I see that the declared type is PlayerPiece, and since PlayerPiece extends Gameshape, that means PlayerPiece inherited the display() method. Therefore, PlayerPiece can be used to invoke the display() method."

Which methods can be invoked when the PlayerPiece object is being referred to using a reference declared as type Animatable? Only the animate() method. Of course the cool thing here is that any class from any inheritance tree can also implement Animatable, so that means if you have a method with an argument declared as type Animatable, you can pass in PlayerPiece objects, SpinningLogo objects, and anything else that's an instance of a class that implements Animatable. And you can use that parameter (of type Animatable) to invoke the animate() method, but not the display() method (which it might not even have), or anything other than what is known to the compiler based on the reference type. The compiler always knows, though, that you can invoke the methods of class object on any object, so those are safe to call regardless of the reference—class or interface—used to refer to the object.

We've left out one big part of all this, which is that even though the compiler only knows about the declared reference type, the JVM at runtime knows what the object really is. And that means that even if the PlayerPiece object's display() method is called using a GameShape reference variable, if the PlayerPiece overrides the display() method, the JVM will invoke the PlayerPiece version! The JVM looks at the real object at the other end of the reference, "sees" that it has overridden the method of the declared reference variable type, and invokes the method of the object's actual class. But one other thing to keep in mind:

Polymorphic method invocations apply only to instance methods. You can always refer to an object with a more general reference variable type (a superclass or interface), but at runtime, the ONLY things that are dynamically selected based on the actual object (rather than the reference type) are instance methods. Not static methods. Not variables. Only overridden instance methods are dynamically invoked based on the real object's type.

Since this definition depends on a clear understanding of overriding, and the distinction between static methods and instance methods, we'll cover those next.




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net