by Al Saganich
This month, we introduce a new feature in Visual J++ Developer's Journal: "Pure Java." Our "Pure Java" articles will cover aspects of the Java language common to all browsers, compilers, and virtual machines. With that in mind, let's looks at our first Java topic—interfaces.
Interface introductionFirst and foremost, an interface provides a method whereby a developer can define a behavior that a class must provide (or, in Java terms, implement). An interface specifies the exact signature of one or more methods. Defining methods allows an interface to specify a behavior but not implement it. In other words, the interface specifies what must be done but not how to do it. Interfaces let you divide projects into manageable sections—for example, separating the way objects are sorted from the way two yet-to-be-defined objects are compared.
You can use interface variables to point to any class that implements the interface. Doing so allows separately developed classes to interact. Interfaces also let Java support a flavor of multiple inheritance, so classes derived from a certain parent class may take on another "class's" (interface's) behavior.
Interfaces offer a feature that Java lacks: the callback (often provided in other languages via pointers to functions). We discuss this feature in the sidebar "Using Callbacks." And as we discuss in "Dynamic Class Loading," interfaces let you load classes dynamically, so you can add to an application a behavior that hasn't yet been developed.
The following items sum up what an interface can and can't do:
Let's work through a simple interface example.
An exampleThe java.util.Enumeration interface is commonly implemented in Java. This interface provides two methods: nextElement, which returns the next object; and hasMoreElements, which returns true or false depending on whether there are additional objects. The code in Listing A creates a simple, singly linked list (with little error checking) that implements the Enumeration interface. Listing B shows portions of some text code for manipulating this list. (You can download our sample files from www.zdjournals.com/vjp as part of the file may98.zip.) The interesting portions of each appear in color.
Listing A: list.java
import java.util.Enumeration;
public class list implements Enumeration
{
// Lists contain instances of nodes.
node head = null;
node current = null;
node first = null;
// Don't you just love inner classes?
// No scoping or name contention problems!
class node
{
. . .
}
public list()
{
head= current = null;
}
public void insert(Object object)
{
. . .
}
/*
* Methods required by the enumeration interface
*/
public boolean hasMoreElements()
{
if (null == first)
return false;
else
return true;
}
public Object nextElement()
{
Object data = first.getData();
first = first.getNext();
return data;
}
}
Listing B: list.java
import list;
public class TestEnumeration
{
/** main() method to invoke from JVM. */
public static void main(String args[])
{
. . .
int i=1;
while (aList.hasMoreElements())
{
System.out.println("Item " + i +
" = " +
((Integer)
aList.nextElement()));
i++;
}
}
}
The list.java lines
import java.util.Enumeration;
public class list implements Enumeration
import the Enumeration interface. The class definition indicates that the list class will provide the methods defined in the interface. And, of course, the class also implements the required methods. Listing B (most of which has been removed to save space) then uses the two methods to walk the list. The listings aren't very interesting, but they show the basic principles of an interface.
Interface syntaxNow, let's look at the syntax for defining an interface, as well as the syntax for defining a class that implements an interface. The syntax for defining an interface is as follows:
[abstract|public] interface interfacename
[extends parent1, [parent2. . .]]
{
// Method definitions
// and
// variable definitions
}
The syntax for defining a class is as follows:
[public | [abstract|final]] class classname
[implements interfacename[,interfacename2
. . .]]] { . . . }
Notice how the syntax shows that the class implements the interface.
For example, you might write the following interface definition:
public interface ImaInterface
{
// Some method
java.lang.String ImA();
}
The sample implementation might then be written
class IImplementImaInterface implements
ImaInterface
{
public java.lang.String ImA()
{
return " IImplementImaInterface";
}
}
We'll return to these definitions later, when we examine dynamic class loading. For now, we'll work through a more concrete example and see how the development of an object can be separated from classes that process the class.
SortingYou can see a good example of using an interface by implementing a sorting algorithm, then defining objects as implementing the interface so they can be sorted. For instance, suppose you need to sort objects but another group is developing the sort. In order for your objects to be sorted with the sort routine, the two groups must agree only on the signature of the interface.
Note: Interface names
When naming an interface, it's considered good form to end the name with able. You might name a sort interface Sortable. Another interface might be named Stretchable or any other able you can come up with!
For example, you can define the Sortable interface as follows:
public interface Sortable
{
int compare(Object against);
}
The interface really needs only a single method, which supports comparing two objects. You can then define objects that implement the Sortable interface and sort them via mechanisms provide by the sorting team.
Listings C and D show this process in action. Listing C contains a sort class with a single BubbleSort method that performs a bubble sort on a list of Sortable objects via the compare method. Note that any object that implements the Sortable interface can be sorted using this method. Listing D then puts this class through its paces.
Listing C Sorts.java
import Sortable;
class Sorts
{
static void BubbleSort( Sortable [] data)
{
for (int i = 0; i < data.length-1;i++)
{
for (int j = 0; j < data.length-I-1; j++)
{
if ( data[j].compare(data[j+1]) >= 0)
{
Sortable temp = data[j];
data[j] = data[j+1];
data[j+1] = temp;
}
}
}
}
}
Listing D tstBubbleSort.java
public class tstBubbleSort
{
public static void main(String args[])
{
class listObject implements Sortable
{
int data;
public int getData()
{
return data;
}
public listObject(int data)
{
this.data = data;
}
public int compare(Object b)
{
return data-
((listObject)b).getData();
}
public String toString()
{
Integer temp = new Integer(data);
return temp.toString();
}
}
listObject data[] = new listObject[10];
Integer temp;
for ( int i = 0; i <data.length; i++)
{
data[i] = new
listObject((int)(Math.random()* 100));
System.out.println("Added " +
data[i].toString() + " to list");
}
System.out.println(
"Data array contains(before)");
for ( int i = 0; i <data.length; i++)
{
System.out.println(data[i].toString());
}
System.out.println("Sorting!");
Sorts.BubbleSort(data);
System.out.println(
"Data array contains(after)");
for ( int i = 0; i <data.length; i++)
{
System.out.println(data[i].toString());
}
}
}
To use the Sorts class and its BubbleSort method, you simply develop a class that implements the Sortable interface. Then, pass an array of Sortable elements to the static sort routine BubbleSort. (In "Dynamic Class Loading," we show that sorting is another excellent use of this process. Someone might define a new super-efficient sort that you could take advantage of even though it didn't exist when you developed your application!)
Comparing interfaces and abstract classesJava supports abstract classes, and on the surface an interface resembles an abstract class—but there are some important differences. Table A lists the more obvious ones.
Table A: Abstract classes vs. interfaces
Abstract classInterfaceCan define methodsCan define methods
Can define constantsCan define constants
Can define variablesCan't define variables.
Can provide partial implementationCan't provide any implementation
May implement interfacesMay extend interfaces.
Normally used where some of Normally used where the definition of
the behavior is common behavior is common but none of the
implementation is.
An abstract class is similar to an interface, but it's used much differently. For instance, in the database example in "Dynamic Class Loading," we open all databases in the same way we might define an abstract class that implements open and close methods. We might also have an interface for read and write methods, then code specific instances of each database type that extends the abstract class and implements the interface.
ConclusionThe Java interface mechanism is quite robust and handles many of the problems associated with multiple inheritance simply and elegantly. Name clashing is forever banished! Interfaces let you easily split definition from implementation, so multiple developers or groups can work on different aspects of the same problem. Using interfaces dynamically allows you to easily extend an application—you can develop the application now and add functionality later! The more you use interfaces, the more you'll appreciate their power.
Copyright © 1998, ZD
Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD
Inc. Reproduction in whole or in part in any form or medium without
express written permission of ZD Inc. is prohibited. All other product
names and logos are trademarks or registered trademarks of their
respective owners.