left-icon

Java Succinctly® Part 2
by Christopher Rose

Previous
Chapter

of
A
A
A

CHAPTER 3

Polymorphism

Polymorphism


Polymorphism is a term that refers to code that might behave differently each time it is executed. There are many types of polymorphism in programming, but the term is often used to refer to a particular mechanism in object-oriented programming. Our objective is to define a parent class with some specific method or methods, then to define multiple child classes that inherit and define different code for the methods. Then, when we execute the method using the child classes, we can use the same code. However, the child classes will each perform their own specific versions of the methods.

In order to illustrate how the same code can behave differently, imagine we are creating a game (we will implement a game in the final chapter of this e-book). In a game, there is often a virtual world populated by objects, nonplayer characters (NPCs), and the player. Each object in the game is able to move, which means we can create a generic GameObject class with an abstract method called move (as per Code Listing 15).

Abstract classes

Code Listing 3.0: GameObject Class

// Abstract parent class:

public abstract class GameObject {

     // Abstract method:

     public abstract void move();

}

Notice that the GameObject class is marked with the abstract keyword. This is a safety measure—we do not want to create any instances from the generic GameObject class, so we mark it as abstract in order to prevent instances being created. Any class with one or more abstract methods must be marked as abstract itself. This class has the move method, which is abstract, so the entire class must be abstract. If you try to create an instance of the GameObject class, Eclipse will give you an error: Cannot instantiate the type GameObject. I call this a safety measure because we could define a body for move in GameObject and remove the abstract keywords altogether, but it may not be wise—in order for an object in our game to be useful, it must be of some specific type, not just a generic nameless “object.”

Notice also that the move method has no body. It consists of nothing more than a function declaration with a semicolon. We are saying that there exists another class, or classes, capable of performing the function move. We may want to refer to instances of these other classes as GameObject objects, but they must specify what the move method does or they will themselves be abstract.

Let us now define some classes that inherit from our GameObject class. When the NPCs move, we must execute different code when the player moves. NPCs are controlled by the computer, and they typically employ some form of AI in order to talk to the player or wander around a town. The player, on the other hand, is not controlled by the computer. The player requires input from the user. So, we could create two derived classes from our GameObject class called NPC and Player, as per Code Listings 3.1 and 3.2.

Code Listing 3.1: NPC Class

public class NPC extends GameObject {

     public void move() {

          System.out.println(

"The shopkeeper wanders around aimlessly...");

     }

}

Code Listing 3.2: Player Class

public class Player extends GameObject {

     public void move() {

          System.out.println("It is the player's move...");

          // Poll the keyboard or read the mouse movements, etc.

     }

}

In Code Listings 3.1 and 3.2, we have defined child classes that inherit from the GameObject class by using the extends keyword. The extends keyword means that all the member variables and the member methods in the GameObject class also belong to this class. What we have created is called a class hierarchy, which is something like a family tree. The parent class sits at the top and defines all the elements that the lower classes inherit (see Figure 9). The lower classes describe more specific items than does the parent. In our case, the parent describes a generic move method and the child classes define what it means.

GameObject Class Hierarchy

Figure 9: GameObject Class Hierarchy

We can now store all the objects in our game in a single collection, and we can iterate through the collection calling the move methods—both are advantages derived from the class hierarchy. We can create instances of NPC and Player and store them in a collection of GameObjects. All NPCs are GameObjects, and all instances of the Player class are also GameObjects. Code Listing 3.3 shows an example main method that employs a collection of GameObjects but that uses polymorphism to call the two different versions of move.

Code Listing 3.3: Polymorphism

public class MainClass {

     public static void main(String[] args) {

          // Create 5 objects in our game.

          GameObject[] gameObjects = new GameObject[5];

          // First object is the player.

          gameObjects[0] = new Player();

          // Other objects are NPCs.

          for(int i = 1; i < 5; i++) {

               gameObjects[i] = new NPC();

          }

          // Call move for all objects in the game.

          for(int i = 0; i < 5; i++) {

               gameObjects[i].move();

          }

     }

}

The line highlighted in yellow in Code Listing 3.3 is an example of polymorphism. The first time the loop iterates, the local variable i will be set to 0, and this line will cause the method Player.move() to be called because the first element of the gameObject array is of the Player type. But the other objects in the gameObjects array are all NPCs, which means the next iterations of this loop will call NPC.move(). The same line of code (i.e. “gameObjects[i].move();”) is being used to call two different methods. We should understand that we did not create any instances from the GameObject class directly. We cannot do this because the GameObject class is abstract. We created instances of the NPC and Player classes, but then we used the generic term GameObject to store them and call their methods.

Upon running the application from Code Listing 3.3, the output is as follows:

It is the player's move...

The shopkeeper wanders around aimlessly...

The shopkeeper wanders around aimlessly...

The shopkeeper wanders around aimlessly...

The shopkeeper wanders around aimlessly...

Note: In Java, child classes can have only one parent class each. Some languages allow multiple parent classes, called multiple inheritance, but Java allows only one. However, Java does allow multiple interfaces to be implemented by a child class (see the next section).

Overriding methods

An abstract parent class can contain member methods and variables. In the previous examples, the GameObject class might contain x and y variables that specify where the object resides. We can define a method called print that outputs some information about the object (see Code Listing 3.4).

Code Listing 3.4: Nonabstract Parent Methods

// Abstract parent class:

public abstract class GameObject {

     // Member variables:

     int x, y;

     

     // Nonabstract method:

     public void print() {

          System.out.println("Position: " + x + ", " + y);

     }

     

     // Abstract method:

     public abstract void move();

}

Any child classes that inherit from the GameObject class in Code Listing 3.4 will automatically have the x and y variables of their parent class. They will also inherit the print method, which is not abstract. If we add a loop to our main method to call print with each of our five objects (Code Listing 3.4), they will each use the only version of the print method so far defined—the parent’s print method.

Code Listing 3.5: Calling the Parent’s Print Method

public class MainClass {

     public static void main(String[] args) {

          // Same code as before

     

          // Call print for all objects in the game.

          for(int i = 0; i < 5; i++) {

               gameObjects[i].print();

          }

     }

}

The output from Code Listing 3.5 is as follows:

Position: 0, 0

Position: 0, 0

Position: 0, 0

Position: 0, 0

Position: 0, 0

However, if we define a print method with the same signature as the parent’s method in one of the child classes, we will see that the child classes can override the parent’s method. Code Listing 3.6 shows the same code as the original Player class, except that this time I have overridden the parent’s print method.

Code Listing 3.6: Overriding a Parent’s Method

public class Player extends GameObject {

     public void move() {

          System.out.println("It is the player's move...");

          // Poll the keyboard or read the mouse movements, etc.

     }

     

     @Override

     public void print() {

          System.out.println("Player position: " + x + ", " + y);

     }

}

First, notice that the @Override annotation is optional. The print method in the Player class of Code Listing 3.6 has exactly the same name as the parent’s print method and exactly the same arguments and return type. Now, when we run our main method, we will see that the Player object calls its own specific version of print, while the NPC objects (which do not define a specific version of the print function) call the parent’s print. We say that the Player class has overridden the print method.

Constructors

Constructors are methods that are used to create new instances of objects. An abstract class can supply a constructor, even though we are not allowed to create instances of it. In Code Listing 3.7, the GameObject class has a constructor defined that sets the x and y values to -1.

Code Listing 3.7: Constructor in an Abstract Parent

// Abstract parent class:

public abstract class GameObject {

     // Member variables:

     int x, y;

     

     // Constructor

     public GameObject() {

          // Set the x and y:

          x = y = -1;

     }

     

     // Nonabstract method:

     public void print() {

          System.out.println("Position: " + x + ", " + y);

     }

     

     // Abstract method:

     public abstract void move();

}

If we change nothing else and run the program, we will see that the parent’s constructor is called automatically for each of the child objects:

Player position: -1, -1

Position: -1, -1

Position: -1, -1

Position: -1, -1Position: -1, -1

However, we can also specify constructors for the child classes. Code Listing 3.8 shows the Player class with its own constructor.

Code Listing 3.8: Constructor for the Player Class

public class Player extends GameObject {

     

     // Constructor

     public Player() {

          x = y = 100;     // Start the player at 100x100.

     }

     

     public void move() {

          System.out.println("It is the player's move...");

          // Poll the keyboard or read the mouse movements, etc.

     }

     

     @Override

     public void print() {

          System.out.println("Player position: " + x + ", " + y);

     }

}

Running the application will show that the Player constructor is called to instantiate the Player object, and the NPCs all call the parent constructor because they do not define their own constructor.

Super keyword

When we need to refer to the parent class from within the child classes, we use the super keyword. As an example, Code Listing 3.9 shows how to call the GameObject constructor from within the Player constructor.

Code Listing 3.9: Child Constructor Calling Super Constructor

     // Constructor

     public Player() {

          // Call the parent's constructor.

          super();

          

          x = y = 100;     // Start the player at 100x100.

     }

When we call the parent’s constructor using super(), it must be the first statement in the child’s constructor. This is only true when calling the parent’s constructor. If you want to call the parent’s version of some other method, you can do so at any point in the child’s overridden method.

instanceof keyword

Before we move on to interfaces, the instanceof keyword can be used to test the type of an object. The main method in Code Listing 3.10 uses the same class hierarchy as before.

Code Listing 3.10: Testing with instanceof

public class MainClass {

     public static void main(String[] args) {

          // Define some object:

          GameObject someObject = new Player();

          // Test if the first object is a GameObject.

          if(someObject instanceof GameObject)

               System.out.println("Object is a GameObject!");

          else

               System.out.println("Not a GameObject...");

          // Test if it is a Player.

          if(someObject instanceof Player)

               System.out.println("Object is a Player!");

          else

               System.out.println("Not a Player...");

          // Test if it is an NPC.

          if(someObject instanceof NPC)

               System.out.println("Object is a NPC!");

          else

               System.out.println("Not an NPC...");

     }

}

In Code Listing 3.10, we create a Player object called someObject. Then we use instanceof to test if the type is GameObject, Player, and NPC. Note that the data type of an object can be more than one thing. The output from the preceding main method shows that someObject is both a Player object and a GameObject. However, it is not an NPC:

Object is a GameObject!

Object is a Player!

Not an NPC...

Interfaces

Abstract methods are something like a contract. We say that any class that derives from a parent is capable of performing the abstract methods it inherits. Interfaces take abstract methods to the extreme.

An interface is similar to an abstract parent class, except that it will contain nothing but abstract methods (i.e. there are no methods specified at all in an interface, only method names). Interfaces do not specify member variables (though they can specify static members or class variables). When we derive a class from an interface, we are saying that the derived class must perform the set of methods specified in the interface (or the derived class must itself be an interface or abstract class).

Interfaces often describe some very general aspect of a class hierarchy. Often, interfaces are introduced as being some ultra-abstract version of a class. But there is a subtle difference between the way that an abstract class is typically used and the way an interface is typically used. Interfaces often describe that some particular activity can be performed using the instances of a class rather than describing that the instances can be used to perform some task. For example, an interface might describe objects that are sortable. Many types of objects are sortable—names can be sorted alphabetically, playing cards, and numbers, for example. But while these objects are sortable, the exact mechanism for comparing each is different. We could implement an interface called Comparable, meaning that any two objects that derive from the interface can be compared and thus a list of them might be sorted. Comparing and sorting objects is very common, and Java includes an interface called Comparable already.

Note: In Java, it is not possible to inherit from multiple parent classes. However, it is perfectly legal to inherit from multiple interfaces.

Begin a new project called Interfaces and add the Point class in Code Listing 3.11.

Code Listing 3.11: The Point Class

public class Point implements Comparable {

     // Member variables:

     public double x, y;

     // Constructor

     public Point(double x, double y) {

          this.x = x;

          this.y = y;

     }

     // Print out some info about the point:

     public void print() {

          System.out.println("X: " + x + " Y: " + y + " Mag: " +

                    Math.sqrt(x*x+y*y));

     }

     

     public int compareTo(Object o) {

          // Firstly, if the second object is not a point:

          if(!(o instanceof Point))

           return 0;

          // Cast the other point:

          Point otherPoint = (Point) o;

          // Compute the absolute magnitude of each point from the origin:

          Double thisAbsMag = Math.sqrt(x * x + y * y);

          Double otherPointAbsMag =Math.sqrt(otherPoint.x * otherPoint.x +

                    otherPoint.y * otherPoint.y);

          return thisAbsMag.compareTo(otherPointAbsMag);

          

          /*

          // Note: Double.compareTo does something like the following:

          

               // If this object has a greater magnitude:

               if(thisAbsMag > otherPointAbsMag) return 1;

               // If this object a smaller magnitude:

               if(thisAbsMag < otherPointAbsMag) return -1;

               // If the object's magnitudes are equal:

               return 0;

          */

     }

}

Notice the keyword implements on the first line of Code Listing 3.11, which is followed by the interface Comparable. This is how we inherit the methods from an interface. We do not use the term extends as we did with classes. The Comparable interface defines a single method that has the signature “public int compareTo(Object o)”. Therefore, in order to implement the Comparable interface, we must supply this method in our class.

When we supply the compareTo method in our class, we have to understand the meaning of the parameters and the output. The method takes a single parameter that is presumably the same data type as the class we are defining (it does not make sense to compare points to playing cards, etc.; we are only interested in sorting points here). I have first supplied a test inside the compareTo method in order to make sure that the object we are comparing is actually a Point. If the object is not a Point, we could throw an exception, but I have returned 0 in Code Listing 3.11, which means the two objects are equal.

Next, we need to specify exactly what it means to compare our objects. If this object is less than o, we return -1. If this object is greater than o, we return 1, and if the objects are the same, we return 0. For each class that implements the Comparable interface, we need to define what it means for the instances to be greater and less than each other. I have selected the meaning to be based on the absolute magnitude of the points (i.e. the distance from 0 on a 2-D plane, which is the square root of (x*x+y*y)). You will notice that I used the boxed version, Double, because the native double type does not implement the Comparable interface, while the boxed version, Double, does. After we have computed the distance between this and o, we call Double.compareTo and return the result. I also included a comment at the end of the code that shows roughly how the Double.compareTo method will behave.

Now that we have a class that implements the Comparable interface, we can create a collection of instances from our Point class and sort them using the standard Java sorting. Next, let’s create a new class called MainClass. The code for this class is presented in Code Listing 3.12.

Code Listing 3.12: Sorting a List of Comparable Objects

import java.util.ArrayList;

import java.util.Collections;

public class MainClass {

     public static void main(String[] args) {

          // The total number of points in the demo:

          int numberOfPoints = 5;

          

          // Create a list of random points:

          ArrayList<Point> points = new ArrayList<Point>();

          for(int i = 0; i < numberOfPoints; i++)

               points.add(new Point(Math.random() * 100,

Math.random() * 100));

          

          // Print the unsorted points:

          System.out.println("Unsorted: ");

          for(int i = 0; i < numberOfPoints; i++)

               points.get(i).print();

          

          // Sorting a collection of Comparable objects:

          Collections.sort(points);

          

          // Print the sorted points:

          System.out.println("Sorted: ");

          for(int i = 0; i < numberOfPoints; i++)

               points.get(i).print();

          

          

          // Sort the items in reverse order (from largest to smallest):

          points.sort(Collections.reverseOrder());

          // Print the points sorted in reverse:

          System.out.println("Sorted in Reverse: ");

          for(int i = 0; i < numberOfPoints; i++)

               points.get(i).print();

     }

}

In Code Listing 3.12, we use Collections.sort and points.sort(Collections.reverseOrder) in order to sort the points and also sort them in reverse order. These sorting methods are designed for use with any objects that implement the Comparable interface. This means we do not have to write a QuickSort (or some other algorithm) and mess around with swapping elements in arrays and comparing them efficiently in an ArrayList. Instead, all we need to do is to implement the Comparable interface and any list of our objects can be sorted!

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.