Polymorphism

Polymorphism by definition means the ability to take many forms. "Poly" means many, and "morph" means form. Now in terms of classes and objects in Java, this means the ability to use an object in a more general form. We can first look at this in terms of casting objects.

Casting Objects

We have already looked at casting in Chapter 2, where, for example, we cast a variable of type int to a variable of type short. We also mentioned implicit and explicit casting, where implicit casting means that the destination variable could safely hold the source value and explicit casting means the use of typecasting code explicitly.

int myInt = 10; long myLong = myInt;             // implicit cast short myShort = (short)myInt     // explicit cast 

With the integer data types, there is a hierarchical relationship between them in terms of their storage sizes. Whether a cast needs to be explicit relies on this relationship. The same can be applied for casting objects. Casting an object to a super class type can be seen as an implicit cast, known generally as "casting up" through the class hierarchy, whereas casting an object to a subclass, known generally as "casting down" through the class hierarchy, can be seen as an explicit cast. For example, regarding the previous example, we can cast an Alien object implicitly to a reference of type Creature, as follows.

Alien myAlien = new Alien("Dak-DakDakDak"); Creature myCreature = myAlien; // implicit cast 'up'

Here we create an object of type Alien. This is the true type of the object. We then declare a reference of type Creature, called myCreature, and assign it to the new Alien object. We could also have simply entered the following code.

Creature myCreature = new Alien("Dak-DakDakDak");

This is an implicit cast because Creature is a super class of Alien. We know that the Alien object can be safely cast to a Creature object because Alien is derived from Creature, so any members of a Creature object exist as part of the Alien object also.

However, the casting of an object does not change the object itself; the object always remains exactly how it is. The only thing that changes is the reference to the object. For example, view the following line of code:

myCreature.speak();

The speak method that is invoked would be the one defined in the Alien class, not the one defined in the Creature class, as the true type of the object still remains of type Alien. Casting the object to any type does not alter the object; it doesn't even alter the type of object it is. It changes the way your program sees the object, as if it were a different type. When you access a member of the object, the member that is accessed is the member closest to the true type of the object, which is why the speak method in the Alien class is invoked and not that of the Creature class (even when we cast the object to type Creature).

Casting an object to a subclass type requires explicit casting. For example:

Creature myCreature = new Alien("Dak-DakDakDak");   //   Explicit cast back to type Alien Alien myAlien = (Alien)myCreature; 

You will need to cast down the class hierarchy when you need to access a member that is specific to the subclass type. For example, the Alien class may contain a method like destroyPlanet, which does not belong to the Creature class like the speak method does and therefore cannot be invoked from a reference of type Creature, like myCreature. It must be invoked from a reference of type Alien, like myAlien. You will more than likely need to cast objects down when using many of the classes in the package java.util. This package provides many classes that can be used for storing lists of objects. These lists contain objects of type Object, which any object can be cast to, casting up to the top of the class hierarchy. When you need to retrieve objects that you've added to these lists, you will need to explicitly cast your objects back to a more descriptive type, back down the class hierarchy, in order to use them properly. In Chapter 5 we will look at packages in general and pay particular attention to the java.util package.

Polymorphism in Action

In the previous example we had four classes: Beings (main class), Creature, Alien, and Human. In this example we are simply going to change the main class to PolymorphicBeings. The classes Creature, Alien, and Human remain exactly the same as they were defined in the previous example, so you will need to get the code for them from the previous example. The main class in this example is the important one, where we will take advantage of polymorphism. Here is the code for PolymorphicBeings.java:

class PolymorphicBeings {     public static void main(String args[])      {         Creature creatureList[] =              {                  new Creature("I'm a creature you know"),                 new Alien("Well I'm an alien, a more specific                     creature"),                 new Creature("Ohh, he thinks he's special"),                 new Human("I'm a human and I know I'm special"),                 new Alien("Again I'm from Jupiter, and I'll eat                     you all")             };           for(int i=0; i<creatureList.length; i++)             creatureList[i].speak();     } }

When you run this code along with the classes Creature, Alien, and Human that we defined earlier, you should get output similar to the following figure.

click to expand
Figure 4-8:

Here we have a list of many different types of objects, which are derived in some way by the Creature class. Two of the objects are instances of the Creature class themselves, whereas the other objects that are created are instances of subclasses of the Creature class. This means that it is safe to call the method speak on any of the objects, as this method belongs to the Creature class. The key is that this method can then be invoked specifically to its object, even though all of the objects are believed to be merely of type Creature. This is a great advantage for listing objects of varied types that you want to treat collectively. Imagine in a game that we had a list of many creatures of various subtypes of Creature, like Alien and Human, and in every game loop we wanted to call a move function on every object in the list. If the move method for an Alien differed from a Human, like the speak methods do, we could simply use a list like this and call the move method specific to the object automatically without needing to find out the exact type of the object that we are dealing with at the time.

Abstract Classes

Abstract:  

Considered apart from any application to a particular object; separated from matter; existing in the mind only.
—Webster's Revised Unabridged Dictionary, © 1996, 1998 MICRA, Inc.

That's a beautiful quote, as I'm sure you'll agree. In object-oriented programming there is often a time when a class is needed solely as the basis for being derived by another class, where it in itself should not be instantiated. In Java this would be an abstract class. An abstract class cannot be instantiated. In order to use an abstract class you must create another class, which extends the abstract class that can then be instantiated. For example, we could say that our Creature class may be declared as abstract if we wanted to prevent any instantiation of it. We would do this by entering the keyword abstract before the keyword class in the class declaration, as follows.

public abstract class Creature {     // code as normal here }

The code encapsulated by the Creature class can stay the same. Making a class abstract simply means that it cannot be instantiated. It must be subclassed with an object derived from the subclass that inherited members of the abstract class from which it is derived. If in our previous example we did make the Creature class abstract, the code would not compile because we were trying to create objects of type Creature inside the main class PolymorphicBeings. If we removed these instantiations, the code would work fine. Polymorphism in that example would not be affected by the fact that the Creature class was abstract.

In the case of the Creature class, it is perfectly feasible that we should have made it an abstract class. This is because there is likely to be no actual object that would be defined as just a Creature but always detailed in a more specific subclass of Creature, like Alien or Human or Insect or whatever we wanted.

Abstract Methods

Abstract methods can only be defined within a class that has itself been declared as abstract. An abstract class does not need to contain an abstract method. However, a class with an abstract method is abstract, regardless of the class declaration. An abstract method is one that is defined but does not contain a code body, basically meaning that it is declared but not defined. So for example, if we are working on the premise of the Creature class being abstract, we could also declare a method in the class as abstract and omit the code body of the method. In the case of the example PolymorphicBeings, we could make the speak method of the Creature class abstract, as follows:

public abstract class Creature {     public Creature(String greeting)     {         setGreeting(greeting);     }       public void setGreeting(String greeting)     {         this.greeting = greeting;     }       public String getGreeting()     {         return greeting;     }       public abstract void speak();       private String greeting; }

As you can see, the method speak has been declared as abstract using the keyword abstract before the return type of the method. Also notice the semicolon at the end of the method signature, and the method does not define a code body.

Not only does the Creature class need to be extended in order to be instantiated, but any subclass of the Creature class must define the method speak with a supporting code body in place. Ideally the Alien and Human classes we have seen in previous examples do just this so it would be easy to plug this abstract version of the Creature class into these examples. Don't forget that a class defining an abstract method can call that method also, like the new Creature class can still call the abstract method speak polymorphically, provided that the actual type of the object that speak is invoked upon is of a subclass of Creature, such as Alien.

If you use an abstract class, you should be sure that you will never need an instance of that class. An abstract class can define normal methods to which it can provide suitable functionality, and should define abstract methods if it does not know how to handle those methods itself. Furthermore, a subclass of this class should be able to provide appropriate code for these abstract methods. If a class cannot provide a suitable implementation for a method, either the class should be abstract along with that method or the method does not belong in the class in the first place.

Note that one abstract class can be extended by another abstract class, where it too can choose whether or not to provide code bodies for inherited abstract methods, if any are inherited of course.

The use of abstract classes and methods is more of a design issue for well thought-out projects, and design issues for games often go out the window when you just want to get the thing working and then tweak the game code from there. This is perfectly normal, especially for programming games in Java. Awareness of all parts of the Java language is important in the long run, especially when you use the standard libraries provided in the Java SDK, which are full of abstract classes and interfaces, classes using those interfaces, and so forth. We're not saying designing games isn't extremely important, but we still like the idea of hacking away at things to learn and then tweaking the code, like the good old days.

The much-preferred alternative to using abstract classes in many respects is the use of interfaces, as we shall discuss now.



Java 1.4 Game Programming
Java 1.4 Game Programming (Wordware Game and Graphics Library)
ISBN: 1556229631
EAN: 2147483647
Year: 2003
Pages: 237

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