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

A small Shape/Circle example

This example illustrates basic inheritance with a Shape class and a Circle class, and it contains a few of the elements discussed here.

Interfaces

Java does not allow multiple inheritance.

Interface - A construct, similar to a class, which can contain only constants, method signatures (abstract), default methods, static methods, and nested types

Format for declaring an interface

  modifier interface Name
  {
    // constant declarations
    // abstract method signatures - keyword "abstract" not needed
    //   for these.  
  }
Example (from libraries we will study later):
 interface MouseMotionListener
 {
    public void mouseDragged(MouseEvent e);
    public void mouseMoved(MouseEvent e);
 }
Note that in this example, the two methods are both abstract. The only methods that would be defined in an interface are those labelled with the modifiers default or static

How to use an interface

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 purpose of an interface is that implementing classes are providing a contract that they will have the features available that are declared in the interface. This will be enforced by the compiler.

Tagging (or marker) interfaces

A tagging interface, also known as a marker interface, is an interface with no constants or methods in it -- i.e. empty. The purpose of such an interface is to set up a rule that applies to specific classes, adding the "is-a" relationship on those classes.

The Cloneable interface

A special interface in the Java.lang package which happens to be empty. This is an example of a marker interface:
  public interface Cloneable
  {
  }
A class can use the clone() method (inherited from class Object) only if it implements Cloneable.

Functional interfaces

A functional interface is a Java interface that contains exactly one (and only one) abstract method. Many such interfaces will be used with Java's lambda expression capabilities.

Some functional interfaces that will appear in course examples:


Examples from textbook

These are a few inheritance and interface examples from Deitel (edition 7, slightly older edition)