Copyright 2003 © by Charlie Calvert
The extensive use of interfaces is one of the most powerful
features of the Java Development Kit. Java takes full advantage of
the power of interfaces and uses them to provide standards that help
us build easily reusable code.
In this two part article I am going to look first at interfaces in
general, and then at one narrow case in which Java uses interfaces
and events to help you provide a means for loosely coupling objects.
This latter technology promotes a simple to use, "plug and
play," type of object reuse.
What is Loose Coupling
The term "loose
coupling" probably cannot be defined in a definitive manner.
It is popularly used to describe the way web services allow clients
and servers to be created in entirely separate development processes.
However, I am not going to use the word in that context.
Instead, I am going to show how interfaces can provide a high
degree of autonomy for individual objects used inside a single
application. The objects I will explore will have very few
dependencies on other objects. In this sense, they are "loosely"
coupled to the other objects in their program. Because the objects
are so autonomous, they will be easy to maintain and easy to reuse.
As this article will show, combining interfaces with events can
provide a simple, easy means to allow developers to promote reuse.
The ultimate goal is to allow the creation of objects that can be
used by multiple client objects in much the same way that a web
service can be used by multiple clients. These loosely coupled
objects will have few direct dependencies binding them together.
Furthermore, the dependencies that they do have should be defined by
clear standards that can be easily replicated by other clients that
wish to consume these objects. The establishment by an object of a
clear standard, of a well defined contract, makes that
object easily reusable.
Thinking about Interfaces
Someone on the Java development team who understood interfaces
decided to make them a big part of the Java SDK. There are hundreds
of examples in the J2SE SDK of the correct way to use interfaces.
This article is going to focus on only a few of them.
Here are two key benefits derived from using interfaces:
An interface provides a means of setting a standard. It
defines a contract that promotes reuse. If an object implements an
interface then that object is promising to conform to a standard. An
object that uses another object is called a consumer. An interface
is a contract between an object and its consumer.
An interface also provides a level of abstraction that makes
programs easier to understand. Interfaces allow developers to start
talking about the general way that code behaves without having to
get in to a lot of detailed specifics.
The next few section of the text will tackle each of these
benefits in turn.
Why an Interface Defines a Contract
Contracts are important because they promote reuse. In the west,
most of us have a contract that when we meet one another in formal
situations we will shake hands as a way of greeting. Having this
contract simplifies the act of meeting someone. In the same way, we
have a contract that states that saying goodbye in a telephone
conversation means that the conversation is over. If that convention,
if that standard, if that contract, did not exist then phone
conversations would be more difficult. The exact same purpose is
served by the contract established by an interface: It provides a
standard way of handling a particular task. A interface provides a
good way of establishing the convention that two
objects should live by when they form a connection.
How an Interface Defines a Contract
You can declare methods in an interface, but you cannot use the
interface to implement those methods. Instead, you use a class to
implement the methods found in one or more interfaces.
Consider the following simple interface:
public interface Runnable
{
public abstract void run();
}
This class provides a declaration for a method called run. It does
not, and cannot, provide an implementation for that method. This
particular interface states that any class that implements
Runnable will contain a method called run that is
declared to be public and void. Here is a class that
implements the Runnable interface:
public class MyClass implements Runnable
{
public void run()
{
System.out.println("I implement the runnable interface");
}
}
In saying that MyClass implements the Runnable
interface, we are saying that it is guaranteed to conform to a
particular standard. That standard states that any class which
implements Runnable must contain a method called run
which is declared to be both public and void.
It should now be clear to you how you can use an interface to
define a contract between an object and its consumer. In particular,
the interface promises that a particular class will contain certain
methods. The interface is a contract between that class and the class
that uses it. The contract states that the implementing class
contains certain methods with certain signatures. It is the basis on
which a relationship can be established between an object and its
consumer.
Capturing Abstractions: Interfaces and UML
By now you might be getting the sense that interfaces aren't
really as complicated as they may have seemed at first. In fact,
there are few concepts in programming that are much simpler than
interfaces. There is no mystery here at all – at least not on
the syntactical level. As the Runnable – MyClass example shows,
the syntax for using an interface in Java is very simple.
Interfaces represent a fairly high level of abstraction. If we
talk about a class that implements Runnable, then we are not talking
about a specific class. We are talking about a group of classes that
implements a particular behavior.
This abstraction can be captured in a UML diagram. In particular,
Figure 1 shows the relationship between the Runnable interface and
MyClass.

Figure 1: The dotted line ending in a closed triangle is the UML
way of saying that MyClass implements that Runnable interface.
Any class that implements an interface can be captured in diagram
similar to the one shown in Figure 1. All one needs to do is draw a
dotted line ending in a closed triangle between the implementing
class and the interface that it implements.
Why is this useful? Why do we care? What is it about this diagram
that adds any value to our program?
A UML diagram is easy to understand. In complex programs, with
dozens or even hundreds of classes and interfaces, it can be very
hard to see the relationship between the classes you have created. As
they say, a picture is worth a thousand words. It is easier to look
at a picture and see the relationship between objects than it is to
study many source files and try to see the relationship between the
chunks of code found in each file.
Interfaces are all about making your life as a programmer easier.
Just as the convention of shaking hands makes it easier to meet
someone new, so does the existence of an interface make it easier to
allow two objects to begin speaking to one another. UML diagrams
make it easier for you to see the relationship between classes.
Defining Abstract Behavior with Interfaces
UML diagrams capture one way in which interfaces can help
programmers deal with complex behavior through relatively easy to
understand abstractions. There is, however, another sense in which
abstractions can be captured by interfaces.
In the J2SE SDK, there are a set of classes all of which implement
the List interface. Examples of these classes include Vector,
ArrayList and LinkedList.
Consider the following simple class:
import java.util.Vector;
import java.util.ArrayList;
import java.util.LinkedList;
public class Untitled1
{
ArrayList arrayList = new ArrayList();
Vector vector = new Vector();
LinkedList linkedList = new LinkedList();
}
The Untitled1 class creates instances of three classes which
implement the List interface. Figure 2 shows what class
Untitled1 looks like in a UML diagram. Figure 3, 4 and 5 show that
ArrayList, Vector and LinkedList all implement
the List interface. Figure 6 shows a UML view of the List
interface.

Figure 2: In this UML dialog a solid line ending in an open arrow
shows that Untitled1 contains instances of the ArrayList, LinkedList
and Vector classes. Compare with the dotted line and closed arrow
symbol shown in Figure 3.

Figure 3. ArrayList implements the List interface.
Figure 4. LinkedList implements the List interface.

Figure 5. Vector implements the List interface.
Figure 6. The List interface as seen in a UML diagram. The
plus signs represent public methods. Compare with Figure 2, where
JBuilder standard icons from the Structure Pane are displayed.
JBuilder allows displaying UML in either mode.
The ArrayList, LinkedList and Vector classes
all conform to the contract established by the List interface. Unlike
the Runnable interface, the List interface declares multiple methods.
By conforming to this contract, the ArrayList, LinkedList
and Vector classes all promise to implement the methods of the
List interface such as add(), get(), indexOf()
and isEmpty(). In other words, these classes all display the
behavior associated with the List interface. Is this sense,
they all belong to the same family.
Most experienced drivers can pilot any reasonably sized car. They
can do this because the interface for a car is the same in most
vehicles, whether that car is a Honda, a Ford or a BMW. In the same
way, most developers who know the List interface can use the
ArrayList, LinkedList and Vector classes. Just
as all cars have a steering wheel and a transmission, so do all
classes that implement the List interface have methods such as
add(), get()
and indexOf(). In this sense, all these classes behave in the
same manner.
To illustrate this point in a practical example, let's extend
MyClass to support the List interface. An example of
how to do this is shown in Listing 1.
Listing 1: A class that uses the List interface.
package untitled10;
import java.util.List;
import java.util.Iterator;
public class MyClass implements Runnable
{
List myList = null;
public MyClass(List myList)
{
this.myList = myList;
}
private String isListEmpty(List myList)
{
if (myList.isEmpty())
return "True";
else
return "False";
}
private void show(List myList)
{
Iterator itr = myList.iterator();
while (itr.hasNext())
{
System.out.println((String)itr.next());
}
}
public void run()
{
isListEmpty(myList);
myList.add("Sam");
myList.add("Mary");
myList.add("Tom");
myList.add("Sue");
Object item = myList.get(1);
System.out.println("Retrieved Item: " + item);
System.out.println("Index of retrieved item: " + myList.indexOf(item));
System.out.println("The list before removing an item called " + item + ":");
show(myList);
myList.remove(item);
System.out.println("The list after removing an item called " + item);
show(myList);
}
}
Notice that the constructor for MyClass now takes an object that
supports the List interface:
List myList = null;
public MyClass(List myList)
{
this.myList = myList;
}
Notice furthermore that the other code in MyClass exercises the List
interface in various ways. The output from this class might look like
this:
Retrieved Item: Mary
Index of retrieved item: 1
The list before removing an item called Mary:
Sam
Mary
Tom
Sue
The list after removing an item called Mary
Sam
Tom
Sue
Any instance of the Vector, ArrayList, or LinkedList class can now be
passed into MyClass. For instance, the following code is perfectly
legal.
ArrayList list = new ArrayList();
Thread thread = new Thread(new MyClass(list));
thread.start();
So is the code shown in this example:
Vector list = new Vector();
Thread thread = new Thread(new MyClass(list));
thread.start();
And so is this code:
LinkedList list = new LinkedList();
Thread thread = new Thread(new MyClass(list));
thread.start();
As you can see, one class, called MyClass, is able to consume
three entirely different classes called Vector, ArrayList
and LinkedList. This is a valuable form of reuse. It is made
possible by the fact that the Vector, ArrayList and
LinkedList classes all support the List interface.
Suppose you create a standard JBuilder application that supports a
class called Frame1 which is a descendant of JFrame. Suppose that
Frame1 contains a method for handling button clicks that looks like
this:
class Frame1 extends JFrame
{
... Code omitted here
void jButton1_actionPerformed(ActionEvent e)
{
LinkedList list = new LinkedList();
Thread thread = new Thread(new MyClass(list));
thread.start();
}
}
Notice that the relationship between Frame1, LinkedList and MyClass
is very abstract. All Frame1 needs to know about MyClass is that it
supports the Runnable interface, and can thus be placed in a thread.
And all that MyClass needs to know about Frame1 is that it knows how
to create a thread. MyClass does not even need to know what type of
class it is being passed in its constructor: new MyClass(list). All
MyClass needs to know is that the class it is being passed
supports the List interface.
All these classes are linked together with a very high degree of
abstraction. The knowledge they have about one another is on a
strictly need to know basis. They don't know any more about each
other than is absolutely necessary for them to interact. In short,
they are relatively loosely coupled.
Summary
In this article you have learned how interfaces can be used to
specify a contract between two classes. This interface provides a
standard means for one class to consume another class.
You also learned that an interface provides a high level of
abstraction that allows you to easily define certain well know
patterns of behavior. Examples of these patterns were illustrated by
the Runnable and List interfaces.
This article also illustrated that objects which conform to a
particular interface can support a form of "loose coupling."
This highly abstracted relationship between two classes supports an
admirable degree of reuse.
This is the end of the first part of this article. In the second
part you will learn how to use a particular interface called
ActionListener to define a way for one class to consume
another class. This relationship will be so loosely coupled that
almost any class can quickly learn to consume any object that
conforms to the contract defined by this interface.