An interface is a very similar to a class, except that (a) it contains no fields, and (b) its methods have no bodies. In other words, it specifies how a class behaves (what methods it provides) without commenting on how the class is implemented. An example of an interface, to represent a domino, is given in Figure 2-23. (We didn't need to represent dominoes in this much detail in Domineering, but this might be useful in other games.)
Figure 2-23. In an interface, there are no fields or method bodies.
1 /** A domino. */ 2 public interface Domino { 3 4 /** Swap the left and right numbers on the Domino. */ 5 public void flip(); 6 7 /** Return the number on the left side of the Domino. */ 8 public int getLeft(); 9 10 /** Return the number on the right side of the Domino. */ 11 public int getRight(); 12 13 } |
A class which provides methods with these signatures can be said to implement the interface. The FieldDomino class (Figure 2-24) implements the Domino interface. Beware of the potential confusion between this Java-specific meaning of the word "implement" and its more general meaning of "provide code for," as in, "Implement this algorithm."
It is okay to leave off comments for the methods specified in the interfacejavadoc is smart enough to copy them from the interface. Indeed, putting the comment in only one place (the interface) reduces the chance that inconsistent comments will appear.
It is possible for more than one class to implement the same interface. The ArrayDomino class (Figure 2-25) represents a domino in a different way. It is also possible for a class to implement
Figure 2-24. The FieldDomino class uses two fields to hold the left and right numbers on the domino.
1 /** A domino. */ 2 public class FieldDomino implements Domino { 3 4 /** The number on the left end of the Domino. */ 5 private int left; 6 7 /** The number on the right end of the Domino. */ 8 private int right; 9 10 /** Initialize the left and right numbers on the Domino. */ 11 public FieldDomino(int left, int right) { 12 this.left = left; 13 this.right = right; 14 } 15 16 public void flip() { 17 int swap = left; 18 left = right; 19 right = swap; 20 } 21 22 public int getLeft() { 23 return left; 24 } 25 26 public int getRight() { 27 return right; 28 } 29 30 public String toString() { 31 return left + "-" + right; 32 } 33 34 } |
more than one interface, as long as it provides all of the methods specified by each of those interfaces.
Domino is a polymorphic type, which can hold a reference to an instance of any class which implements the Domino interface. Thus, if we declare a variable
Domino bone;
we can say either
bone = new FieldDomino(2, 3);
Figure 2-25. The ArrayDomino class uses an array of two ints instead of two int fields.
1 /** A domino. */ 2 public class ArrayDomino implements Domino { 3 4 /** The numbers on the Domino. */ 5 int[] numbers; 6 7 /** Index of the left number. The other is the right number. */ 8 int leftIndex; 9 10 /** Initialize the left and right numbers on the Domino. */ 11 public ArrayDomino(int left, int right) { 12 numbers = new int[] {left, right}; 13 leftIndex = 0; 14 } 15 16 public void flip() { 17 leftIndex = 1 - leftIndex; 18 } 19 20 public int getLeft() { 21 return numbers[leftIndex]; 22 } 23 24 public int getRight() { 25 return numbers[1 - leftIndex]; 26 } 27 28 public String toString() { 29 return numbers[leftIndex] + "-" + numbers[1 - leftIndex]; 30 } 31 32 } |
or:
bone = new ArrayDomino(2, 3);
The relationship between the Domino interface and the FieldDomino and ArrayDomino classes is shown in Figure 2-26. It is not necessary to list the interface methods in the boxes for the classes.
Figure 2-26. UML class diagram showing the relationship between the Domino interface and the FieldDomino and ArrayDomino classes. The dashed, hollow-headed arrows say that both classes implement the interface. The methods in the Domino interface are in italic to emphasize that they are only required, not provided, by the interface.
(This item is displayed on page 58 in the print version)
An interface used to specify all of the important methods of any implementing class is sometimes called an abstract data type or ADT. If you are going to write a class involving variables of type Domino and I am going to write an implementation of the Domino interface, we can start by writing the ADT. As long as I provide the required methods and you do not expect any others, our classes will work together perfectly when they are done.
Many software engineers argue that a class implementing an abstract data type should provide only the methods required by the interface. (There are a few exceptions: constructors, nonpublic methods, common methods like toString(), and main() methods for testing.) If an implementation provides some other public method (say, isDoublet() to determine if both numbers on a Domino are the same), any code which takes advantage of this method will break if we switch to a different implementation which does not provide it.
There are other uses for interfaces besides specifying abstract data types. For example, the Comparable interface (Chapter 8) is used to specify that an object can be compared to other instances of its class. Since Comparable is a polymorphic type, it is possible to write one sorting method which works on any array of Comparables. The interface requires only one method, compareTo(), but nobody would expect a class to have only this method. Classes such as Integer and String are designed for other purposes and also implement Comparable.
Exercises
2.20 |
Draw UML instance diagrams of the objects created by the expressions new FieldDomino(0, 6) and new ArrayDomino(0, 6) |
2.21 |
Through experimentation, determine whether we have to say implements Domino at the top of FieldDomino in order to make (a reference to) an instance of FieldDomino a legitimate value for a variable of type Domino. Is it enough to supply methods with the correct signatures? |
2.22 |
Discuss whether it makes sense for an interface to implement another interface. How about a class implementing another class? |
2.23 |
Through experimentation, determine whether, when a class provides a method required by an interface, the argument names in the class have to match those in the interface. |
2.24 |
Discuss whether it makes sense for an interface to include a constructor. |
Overloading |
Part I: Object-Oriented Programming
Encapsulation
Polymorphism
Inheritance
Part II: Linear Structures
Stacks and Queues
Array-Based Structures
Linked Structures
Part III: Algorithms
Analysis of Algorithms
Searching and Sorting
Recursion
Part IV: Trees and Sets
Trees
Sets
Part V: Advanced Topics
Advanced Linear Structures
Strings
Advanced Trees
Graphs
Memory Management
Out to the Disk
Part VI: Appendices
A. Review of Java
B. Unified Modeling Language
C. Summation Formulae
D. Further Reading
Index