The Essence of OOP using Java, Polymorphism and Interfaces, Part 1

According to Baldwin, if you don't understand the Java interface, you don't understand Java.  There is very little, if anything useful that can be done using Java without understanding and using the Java interface.

Published:  March 27, 2002
By Richard G. Baldwin

Java Programming Notes # 1616


Preface

This lesson is one of a series of lessons designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the group was entitled The Essence of OOP Using Java, Objects, and Encapsulation.  That lesson, and each of the lessons following that one, has provided explanations of certain aspects of the essence of Object-Oriented Programming using Java.  The previous lesson was entitled The Essence of OOP using Java, Polymorphism and the Object Class.

Necessary and significant aspects

This miniseries will describe and discuss the necessary and significant aspects of OOP using Java.

If you have a general understanding of computer programming, you should be able to read and understand the lessons in this miniseries, even if you don't have a strong background in the Java programming language.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at Baldwin's Java Programming Tutorials.

What is OOP?

OOP is the common abbreviation for Object-Oriented Programming.

What is polymorphism?

The meaning of the word polymorphism is something like one name, many forms.

How does Java implement polymorphism?

Polymorphism manifests itself in Java in the form of multiple methods having the same name.

In some cases, multiple methods have the same name, but different formal argument lists (overloaded methods, which were discussed in a previous lesson).

In other cases, multiple methods have the same name, same return type, and same formal argument list (overridden methods).

Three distinct forms of polymorphism

From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in Java:

Preview

Method overloading

I covered method overloading as one form of polymorphism (compile-time polymorphism) in a previous lesson.  I also explained automatic type conversion and the use of the cast operator for type conversion in a previous lesson.

Method overriding and class inheritance

I discussed runtime polymorphism implemented through method overriding and class inheritance in more than one previous lesson.

Using the Java interface

In this lesson and the next, I will explain runtime polymorphism as implemented using method overriding and the Java interface.

A very important concept

In my opinion, this is one of the most important concepts in Java OOP, and the one that seems to give students the greatest amount of difficulty.  Therefore, I will try to take it slow and easy.  As usual, I will illustrate the concept using sample programs.

I will also tie this concept back to the concept of polymorphism using method overriding through inheritance.

A skeleton program

I will present a simple skeleton program that illustrates many of the important aspects of polymorphic behavior based on the Java interface.

Multiple inheritance and the cardinal rule

I will explain how the implementation of interfaces in Java is similar to multiple inheritance.

I will explain the cardinal rule of interface implementation.

A new relationship

I will explain that objects instantiated from classes that implement the same interface have a new relationship that goes beyond the relationship imposed by the standard class hierarchy.

One object, many types

I will explain that due to the combination of the class hierarchy and the fact that a class can implement many different interfaces, a single object in Java can be treated as many different types.  However, for any given type, there are restrictions on the methods that can be invoked on the object.

Many classes, one type

I will explain that because different classes can implement the same interface, objects instantiated from different classes can be treated as a common interface type.

Interfaces are critical to Java programming

I will suggest that there is little if anything useful that can be done in Java without understanding and using interfaces.

In support of this suggestion, I will discuss several real-world examples of the use of the Java interface, including the Delegation Event Model and Remote Method Invocation.

Discussion and Sample Code

Listing 6 near the end of the lesson contains a very simple program named Poly05.

The purpose of this program is to illustrate polymorphic behavior using interfaces in addition to class inheritance.

Designed to illustrate structure

This is a skeleton program designed solely to illustrate the inheritance and interface implementation structure in as simple a program as possible (I will put some meat on this skeleton using another program in the next lesson).

Empty methods

Except for the two methods that return type String, all of the methods in the program are empty.  (Methods that return type String cannot be empty.  They must contain a return statement in order to compile successfully.)

Interface definitions

Listing 1 shows the definition of two simple interfaces named I1 and I2.
 
interface I1{
  public void p();
}//end interface I1
//===================================//

interface I2 extends I1{
  public void q();
}//end interface I2

Listing 1

Similar but different

An interface definition is similar to a class definition.  However, there are some very important differences.

No single hierarchy

To begin with, unlike the case with classes, there is no single interface hierarchy.  Also, multiple inheritance is allowed when extending interfaces.

A new interface can extend none, one, or more existing interfaces.  In this case, I2 extends I1, but I1 doesn't extend any other interface (and unlike classes, an interface doesn't automatically extend another interface by default).

Two kinds of members allowed

Only two kinds of members are allowed in an interface definition:

Each of the interfaces in Listing 1 declares an implicitly abstract method (an abstract method does not have a body).

Neither of the interfaces in Listing 1 declares any variables (they aren't needed for the purpose of this lesson).

A new data type

I told you earlier that when you define a new class, you cause a new data type to become available to your program.  The same is true of an interface definition.  Each interface definition constitutes a new type.

The class named A

Listing 2 defines a very simple class named A, which in turn defines two methods named toString and x.
 
class A extends Object{
  public String toString(){
    return "toString in A";
  }//end toString()
  //---------------------------------//
  
  public String x(){
    return "x in A";
  }//end x()
  //---------------------------------//
}//end class A

Listing 2

Overridden toString

The method named toString in Listing 2 is actually an overridden version of the method having the same name that is originally defined in the class named Object(Recall that a previous lesson made heavy use of overridden versions of the toString method.)

New method

The method named x is newly defined in the class named A. (The method named x is not inherited into the class named A, because the class named Object does not define a method named x.)

The class named B

Listing 3 contains material that is new to this lesson.
 
class B extends A implements I2{
  public void p(){
  }//end p()
  //---------------------------------//
  
  public void q(){
  }//end q();
  //---------------------------------//
}//end class B

Listing 3

Implementing an interface

Listing 3 defines a class named B, which extends the class named A, and implements the interface named I2.

As you already know, a class in Java can extend only one other class.  However, a Java class can implement any number of interfaces. (Multiple inheritance is allowed with interfaces.)

Similar to an abstract class

An interface is similar, but not identical, to an abstract class.

(An abstract class may contain abstract methods or concrete methods, or a combination of the two.  However, an abstract class cannot be instantiated.  An interface also cannot be instantiated.)
For example, all methods declared in an interface must be (are implicitly) abstract, but that is not true for an abstract class.  An abstract class can also contain fully-defined (concrete) methods.

A totally abstract class

At the risk of offending the purists, I will coin a new term here and say that an interface is similar to a totally abstract class (one which contains only abstract method declarations and final variables).

To a first degree of approximation then, we might say that the class named B is not only a subclass of the class named A, it is also a subclass of the totally abstract class named I2. (This is pretty far out in terms of terminology, so to avoid being embarrassed, you probably shouldn't repeat it to anyone else.)

Since I2 extends I1, we might also say that the class named B is a subclass of the totally abstract class named I1.

A different kind of thinking

With this kind of thinking, we have suddenly make it possible for Java classes to support multiple inheritance, with the stipulation that all but one of the inherited classes must be totally abstract classes.

Be very careful with this way of thinking

However, we need to be very careful with this kind of thinking.  While it may help some students to understand the role of interfaces in Java, there are probably some hidden dangers lurking here.

Back to the safety zone

The safest course of action is to simply say that the class named B:

Java does not support multiple inheritance, but it does allow you to extend one class and implement any number of interfaces.

The cardinal rule

The cardinal rule in implementing interfaces is:

If a class implements an interface, it must provide a concrete definition for all the methods declared by that interface, and all the methods inherited by that interface.  Otherwise, the class must be declared abstract and the definitions must be provided by a class that extends the abstract class.
What does that mean in this case?

In this case, this means that the class named B must provide concrete definitions for the methods named p and q, because:

As in method overriding, the signature of the method in the defining class must match the signature of the method as it is declared in the interface.

Class B satisfies the cardinal rule

As you can see from Listing 3, the class named B does provide concrete (but empty) definitions of the methods named p and q.

(As mentioned earlier, I made the methods empty in this program for simplicity.  However, it is not uncommon to define empty methods in classes that implement interfaces, which declare a large number of methods, such as the MouseListener interface.  See my tutorial lessons on event-driven programming for examples.)
The class named C

Listing 4 defines a class named C, which extends Object, and also implements I2.  As in the case of the class named B, this class must, and does, provide concrete definitions for the methods named p and q.
 
class C extends Object implements I2{
  public void p(){
  }//end p()
  //---------------------------------//
  
  public void q(){
  }//end q();
  //---------------------------------//
}//end class B

Listing 4

A driver class

Finally, the driver class named Poly05 defines an empty main method.
 
public class Poly05{
  public static void main(
                        String[] args){
  }//end main
}//end class Poly05
//===================================//

Listing 5

Doesn't do anything

As mentioned earlier, the purpose of this program is solely to illustrate an inheritance and interface structure.  This program can be compiled and executed, but it doesn't do anything useful.

A new relationship

At this point, it might be useful for you to sketch out the structure in a simple hierarchy diagram.

If you do, you will see that implementation of the interface named I2 by the classes named B and C, has created a relationship between those two classes that is totally independent of the normal class hierarchical relationship.

What is the new relationship?

By declaring that both of these classes implement the same interface named I2, we are guaranteeing that an object of either class will contain concrete definitions of the two methods declared in the interfaces named I2 and I1.

Furthermore, we are guaranteeing that objects instantiated from the two classes can be treated as the common type I2.

(Important: references to any objects instantiated from classes that implement I2, can be stored in reference variables of the type I2, and any of the interface methods can be invoked on those references.)
We know the user interface

The signatures of the interface methods in the two classes must match the signatures declared in the interfaces.

This means that if we have access to the documentation for the interfaces, we also know the signatures of the interface methods for objects instantiated from any class that implements the interfaces.

Different behavior

However, and this is extremely important, the behavior of the interface methods as defined in the class named B may be (and often will be) entirely different from the behavior of the interface methods having the same signatures as defined in the class named C.

Possibly the most powerful concept in Java

This is possibly the most powerful (and most difficult) concept embodied in the Java programming language.

If you don't understand interfaces, ...

I usually tell my students several times each semester that if they don't really understand interfaces, they don't really understand Java.

It is unlikely that you will ever be successful as a Java programmer without an understanding of interfaces.

There are very few worthwhile programs that can be written in Java without an understanding of interfaces.

The core aspect

So, what is the core aspect of this concept that is so powerful?

I told you earlier that each interface definition constitutes a new type.  As a result, a reference to any object instantiated from any class that implements a given interface can be treated as the type of the interface.

So what!

When a reference to an object is treated as an interface type, any method declared in, or inherited into that interface can be invoked on the reference to the object.

However, the behavior of the method when invoked on references to different objects of the same interface type may be very different.  In the current jargon, the behavior is appropriate for the object on which it is invoked.

One object, many types

Furthermore, because a single class can implement any number of different interfaces, a single object instantiated from a given class can be treated as any of the interface types implemented by the class from which it is instantiated.  Therefore, a single object in Java can be treated as many different types.

(However, when an object is treated as an interface type, only those methods declared in that interface can be invoked on the object.  To invoke other methods on the object, it necessary to cast the object's reference to a different type.)
Treating different types of objects as a common type

All of this also makes it possible to treat objects instantiated from widely differing classes as the same type, provided that all of those classes implement the same interface.

Important:  When an interface method is invoked on one of the objects using the reference of the interface type, the behavior of the method will be as defined by the author of the specific class that implemented the interface.  The behavior of the method will often be different for different objects instantiated from different classes that implement the same interface.
Receiving parameters as interface types

Methods can receive parameters that are references of interface types.  In this case, the author of the code that invokes interface methods on the incoming reference doesn't need to know, and often doesn't care, about the name of the class from which the object was instantiated.  (For a discussion of this capability, see my tutorials on Java Data Structures on my web site.)

A common example

A very common example is to store references to objects instantiated from different classes, (which implement the same interface) in some sort of data structure (such as list or a set) and then to invoke the same methods on each of the references in the collection.

Heart of the Delegation Event Model

For example, this methodology is at the heart of the Delegation Event Model, which forms the basis of Graphical User Interfaces and event-driven programming in Java.

This often entails defining classes that implement standard interfaces such as MouseListener, WindowListener, TextListener, etc.  In this case, the programmer defines the interface methods to be appropriate for a listener object instantiated from a specific class.  Then a reference to the listener object is registered on an event source as the interface type.

Later when an event of that type occurs, the source object invokes one or more interface methods on the listener object using the reference of the interface type.  The event source object doesn't know or care about the class from which the object was instantiated.  In fact, it doesn't even care how the interface method behaves when it is invoked.  The responsibility of the source object ends when it invokes the appropriate interface method on the listener object.

Model View Control

This same methodology is also critical to the use of the Model View Control paradigm in Java using the Observer interface and the Observable class.  In this case, view objects instantiated from different classes that implement the Observer interface can register themselves on a model object that extends the Observable class.  Then each time the data being maintained in the model changes, each of the views will be notified so that they can update themselves.

JavaBeans Components

This concept is also critical to the use of bound and constrained properties in JavaBeans Components.  One bean can register itself on other beans to be notified each time the value of a bound or constrained property changes.  In the case of constrained properties, the bean that is notified has the option of vetoing the change.

Remote Method Invocation

Java RMI depends totally on the use of Java interfaces.  In this case, one implementation of an interface on a remote computer typically implements the methods as business methods.  Another object on a different computer implements the interface methods as communication methods (remember, polymorphism means something like the same name, many forms).

Java Collections Framework

The Java Collections Framework is also totally dependent on the use of interfaces.  As I mentioned earlier, you can read all about this in my lessons on Java Data Structures.

Iterators and Enumerators

If you appreciate data structures, you will also appreciate iterators.  In Java, Iterator is an interface, and an object that knows how to iterate across a data structure is an object of a class that implements the Iterator interface.

As a result, the users of the concrete implementations in the Java Collections Framework don't need to know any of the implementation details of the collection to create and use an iterator.  All of the work necessary to properly create an iterator is done by the author of the class that implements the appropriate Collection interfaces.  All the user needs to understand is the behavior of the three methods declared in the Iterator interface.

Summary

Polymorphic behavior, based on the Java interface, is one of the most important concepts in Java OOP

In this lesson, I began my discussion of runtime polymorphism as implemented using method overriding and the Java interface.

I presented a simple skeleton program that illustrated many of the important aspects of polymorphic behavior based on the Java interface.

By using a nonstandard notation of my own design, (a totally abstract class), I explained how the implementation of interfaces in Java is similar to multiple inheritance.

I explained the cardinal rule, which is:

If a class implements an interface, it must provide a concrete definition for all the methods declared by that interface, and all the methods inherited by that interface.  Otherwise, the class must be declared abstract and the definitions must be provided by a class that extends the abstract class.
I explained that objects instantiated from classes that implement the same interface have a new relationship that goes beyond the relationship imposed by the standard class hierarchy.

I explained that due to the combination of the class hierarchy and the fact that a class can implement many different interfaces, a single object in Java can be treated as many different types.  However, for any given type, there are restrictions on the methods that can be invoked on the object.

I also explained that because different classes can implement the same interface, objects instantiated from different classes can be treated as a common interface type.

I suggested that there is little if anything useful that can be done in Java without understanding and using interfaces.

Finally I discussed some real-world examples of the use of the Java interface:

What's Next?

In the next lesson, I will discuss a more substantive program as I continue my discussion of polymorphic behavior using the Java interface.

Complete Program Listing

A complete listing of the sample program is shown in Listing 6 below.
 
/*File Poly05.java
Copyright 2002, R.G.Baldwin

This program illustrates polymorphic 
behavior using interfaces in addition
to class inheritance.

This version of the program is 
designed solely to illustrate the
inheritance and implementation 
structure.  Except for the methods
that return String type, all of the 
methods in the program are empty.  
(Methods that return type String
cannot be empty.  They must contain
a return statement in order to
compile successfully.)

The program compiles and executes
successfully under JDK 1.3, but
produces no output.
**************************************/

interface I1{
  public void p();
}//end interface I1
//===================================//

interface I2{
  public void q();
}//end interface I2
//===================================//

class A extends Object{
  public String toString(){
    return "toString in A";
  }//end toString()
  //---------------------------------//
  
  public String x(){
    return "x in A";
  }//end x()
  //---------------------------------//
}//end class A
//===================================//

class B extends A implements I2{
  public void p(){
  }//end p()
  //---------------------------------//
  
  public void q(){
  }//end q();
  //---------------------------------//
}//end class B
//===================================//

class C extends Object implements I2{
  public void p(){
  }//end p()
  //---------------------------------//
  
  public void q(){
  }//end q();
  //---------------------------------//
}//end class B
//===================================//

public class Poly05{
  public static void main(
                        String[] args){
  }//end main
}//end class Poly05
//===================================//

Listing 6


Copyright 2002, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com

-end-