Polymorphism and Interfaces

Polymorphism and Dynamic Binding

If a piece of code is designed to work with an object of type X, it will also work with an object of a class type that is derived from X (any subclass of X). This is a feature known as polymorphism and is implemented by the Java interpreter through a mechanism called dynamic binding. Examples:

Another example

Notice that a useful application of polymorphism is to store many related items, but with slightly different types (i.e. subclasses of the same superclass), in one storage container -- for example, an array -- and then do common operations on them through overridden functions.

Example: Assume the setup in the previous example, base class Shape and derived classes Rectangle, Circle, Triangle. Suppose the base class has a findArea() method (probably abstract, since we don't know how to compute the area for a generic shape), and each derived class has its own findArea() method.

  Shape[] list = new Shape[size];	// create an array of Shape reference variables
  list[0] = new Circle();		// attach a Circle to first array slot
  list[1] = new Rectangle();		// attach a Rectangle to second slot
  list[2] = new Triangle();		// attach a Triangle to third slot

  // ....   continue in a like manner, attaching a variety of different
  //        shapes to the array.  Note that there could be MANY subcategories
  //        of shapes and MANY array elements.  Notice that we are using 
  //        the polymorphism feature

  for (int i = 0; i < list.length; i++)
     System.out.println("The area of shape # " + i + " = " + list[i].findArea()) 
Note that in this for-loop, the appropriate area methods are called for each shape attached to the array, without the need for separate storage for different shape types (i.e. no need for an array of circles, and a separate array of rectangles, etc).

Casting

Since a derived object can always be attached to a corresponding base class reference variable, this is a type of casting that is implicitly allowed. Similarly, direct assignment between variables (derived type assigned into base type) in this order is also allowed, as are explicit cast operations.
  Shape s1, s2;		// Shape is the base class
  Circle c;		// Circle is a derived class

  s1 = new Circle();	// automatically legal (as seen above)

  s2 = c;		// automatically legal
  s1 = (Shape)c;	// explicit cast used, but equivalent to above
To convert an instance of a superclass (base) to an instance of a subclass (derived), the explicit cast operation must be used:
  c = s1;		// would be illegal -- cast needed

  c = (Circle)s1;	// legal (though not always so useful)

The instanceof operator

The instanceof operator checks to see if the first operand (a variable) is an instance of the second operand (a class), and returns a response of type boolean.
  Shape s1;
  Circle c1;
  // other code.....

  if (s1 instanceof Circle)
     c1 = (Circle)s1;		// cast to a Circle variable

Interfaces

Format for declaring an interface:
  modifier interface Name
  {
    // constant declarations
    // abstract method signatures - keyword "abstract" not needed
    //   for these.  ALL methods in an interface are abstract
  }
Use the keyword implements to state that a class will use a certain interface. In this example, Comparable is the name of an interface. The class ComparableCircle inherits the data from the Comparable interface, and would then need to implement the methods (to be able to use them).
  class ComparableCircle extends Circle implements Comparable
  {
    // ....
  }
Other rules:

The Cloneable interface

A special interface in the Java.lang package which happens to be empty:
  public interface Cloneable
  {
  }
This is a marker interface -- no data or methods, but special meaning in Java. A class can use the clone() method (inherited from class Object) only if it implements Cloneable.

Code Examples from Deitel, Ch. 10