JavaEnterprise JavaThe Essence of OOP Using Java, Polymorphism and Interfaces, Part 1

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

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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:

  • Method overloading
  • Method overriding through inheritance
  • Method overriding through the Java interface

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:

  • Methods, which are implicitly abstract
  • Variables, which are implicitly constant (final)

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:

  • Extends the class named A
  • Implements the interface named I2 directly
  • Implements the interface named I1 through inheritance

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:

  • The class named B implements the interface named I2
  • The method named q is declared in the interface named I2
  • The interface named I2 extends the interface named I1
  • The method named p is declared in the interface named I1

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:

  • Delegation event model
  • Model View Control paradigm
  • Bound and constrained properties in JavaBeans Components
  • Remote Method Invocation
  • Java Collections Framework
  • Iterators and Enumerators

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

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories