by Joe Weber and Mike Afergan
Interfaces are Java's substitute for C++'s feature of multiple inheritance, the practice of allowing a class to have several superclasses. While it is often desirable to have a class inherit several sets of properties, for several reasons the creators of Java decided not to allow multiple inheritance. Java classes, however, can implement several interfaces, thereby enabling you to create classes that build upon other objects without the problems created by multiple inheritance.
Somewhat resembling classes in syntax, interfaces are used when you want to define a certain functionality to be used in several classes, but are not sure exactly how this functionality will be defined by these classes. By placing such methods in an interface, you are able to outline common behavior and leave the specific implementation to the classes themselves. This makes using interfaces instead of classes a better choice when dealing with advanced data handling.
Interfaces are the underprivileged first cousins of classes. While classes have the ability to define an object, interfaces define a set of methods and constants to be implemented by another object. From a practical viewpoint, interfaces help to define the behavior of an object by declaring a set of characteristics for the object. For example, knowing that a person is an athlete does not define her entire personality, but does ensure that she has certain traits and capabilities.
As an example, say an athlete will always have a 100-meter time, be able to perform the task of running a mile, and be able to lift weights. By later implementing the athlete interface, you ensure that a person will possess these abilities.
Thinking of interfaces in another way, consider your radio, TV, and computer speakers. Each of them has one common control--volume. For this reason, you might want all these devices to implement an interface called VolumeControl.
Interfaces have one major limitation: They can define abstract methods and final fields, but cannot specify any implementation for these methods. For methods, this means that when writing a method, the body is empty. The classes that implement the interface are responsible for specifying the implementation of these methods. This means that, unlike extending a class, when you implement a method, you must override every method in the interface.
In general, interfaces enable you as a programmer to define a certain set of functionality without having any idea as to how this functionality will be later defined. For example, if a class implemented the java.lang.Runnable interface, it is known to have a run() method. Because the VM can be assured that any Runnable class has a run() method, the VM can blindly call the run() method. At the same time, when the designers were writing the VM, they did not have to know anything about what would happen in the run() method. So, you could be doing an animation, or calculating the first 1,000 prime numbers. It doesn't matter; all that does matter is that you will be running, and you have established that by implementing the Runnable interface.
Another excellent example is the java.applet.AppletContext interface. This interface defines a set of methods that returns information regarding the environment in which an applet is running. For instance, the AppletContext defines a method called getImage. Any viewer capable of running an applet has a means to load an image.
The problem is that different viewers such as the Appletviewer or Netscape Navigator do this differently. Worse yet, even the same browser varies based on the platform it is run- ning on. Fortunately, every browser implements the AppletContext interface, so while the java.applet.Applet class depends on the methods declared in the AppletContext interface, it does not need to worry about how these methods work. That means, you can use the same applet class and the same methods (such as java.applet.Applet.getImage()) in a variety of environments and browsers without worrying about whether the getImage() method will be there.
The syntax for creating an interface is extremely similar to that for creating a class. However, there are a few exceptions. The most significant difference is that none of the methods in your interface may have a body, nor can you declare any variables that will not serve as constants. Nevertheless, there are some important things that you may include in an interface definition.
An example interface is shown in Listing 12.1. It shows three items: an interface, a class that implements the interface, and a class that uses the derived class. Look it over to get an idea as to how interfaces are used and where we are going in this chapter. As you go on, you can thoroughly examine each portion.
public interface Product { static final String MAKER = "My Corp"; static final String PHONE = "555-123-4567"; public int getPrice(int id); } public class Shoe implements Product { public int getPrice(int id) { if (id == 1) return(5); else return(10); } public String getMaker() { return(MAKER); } } public class Store { static Shoe hightop; public static void init() { hightop = new Shoe(); } public static void main(String argv[]) { init(); getInfo(hightop); orderInfo(hightop); } public static void getInfo(Shoe item) { System.out.println("This Product is made by "+ item.MAKER); System.out.println("It costs $" + item.getPrice(1) + `\n'); } public static void orderInfo(Product item) { System.out.println("To order from " + item.MAKER + " call " + item.PHONE + "."); System.out.println("Each item costs $" + item.getPrice(1)); } }
Interface declarations have the syntax
public interface NameofInterface extends InterfaceList
where everything in italics is optional.
Public Interfaces
TIP: Just like public classes, public interfaces must be defined in a file named NameOf Interface.java.
TIP: While only required for public interfaces, it is a good practice to place all interfaces in a file named NameOf Interface.java. This enables both you and the Java compiler to find the source code for your class. Thus, while the Product interface is not public, you should still declare it in a file named Product.java.
The one major rule that interfaces must obey when extending other interfaces is that they may not define the body of the parent methods, any more than they can define the body of their own methods. Any class that implements the new interface must define the body of all of the methods for both the parent and child interface.
As an example, the following lines are the declarations of two separate interfaces, which extend a previously defined interface (Runnable):
interface MonitoredRunnable extends java.lang.Runnable { boolean isRunning() { } }
The declaration shows a more detailed Runnable interface, including some of the features that can be found in java.lang.Thread.
NOTE: Interfaces cannot extend classes. There are a number of reasons for this, but probably the easiest to understand is that any class that the interface would be extending would have its method bodies defined. This violates the "prime directive" of interfaces.
class Fireworks implements MonitoredRunnable { private boolean running; // Keeps track of state. void run() { shootFireWorks(); } boolean isRunning() { // Provides access to other objects without return(running); //allowing them to change the value of running. } }
Because Fireworks implements MonitoredRunnable, it must override isRunning(), declared in MonitoredRunnable. Because MonitoredRunnable extends Runnable, it must also override run(), declared in Runnable.
NOTE: While classes implement interfaces to inherit their properties, interfaces extend other interfaces. When extending more than one interface, separate each by a comma. This means that while classes cannot extend multiple classes, interfaces are allowed to extend multiple interfaces:
interface MonitoredRunnable extends java.lang.Runnable,java.lang.Cloneable { boolean isRunning() { } }
The body of an interface cannot specify the specific implementation of any methods, but it does specify their properties. In addition, interfaces may also contain final variables.
For example, declaring the MAKER variable in the Product interface allows you to declare a constant that will be employed by all classes implementing the Product interface.
Another good example of final fields in interfaces can be found in the java.awt.image. ImageConsumer interface. The interface defines a set of final integers that serve as standards for interpreting information. Because the RANDOMPIXELORDER variable equals 1, classes that implement the ImageConsumer interface can make reference to the variable and know that the value of 1 means that the pixels will be sent in a random order. This is shown in the setHints method of Listing 12.3.
public class MagnaImage implements ImageConsumer{ imageComplete(int status) { ... } setColorModel(ColorModel cm) { ... } setDimensions(int x, int y) { ... } setHints(int hints) { if ((hints & RANDOMPIXELORDER)!=0){ ... } } setPixels(int x, int y, int w , int h, ColorModel cm , byte pixels[], int off, int scansize) { ... } setPixels(int x, int y, int w, int h, ColorModel cm, int pixels[], int off, int scansize) { ... } setProperties(Hashtable props) { ... } }
Methods
public int getPrice(int id); public void showState();
However, in a class, they would require method bodies:
public int getPrice(int id) { if (id == 1) return(5); else return(10); } public void showState() { System.out.println("Massachusetts"); }
The method declaration does not determine how a method will behave; it does define how it will be used by defining what information it needs and what (if any) information will be returned. The method that is actually defined later in a class must have the same properties as you define in the interface. To make the best use of this fact, it is important to carefully consider factors like return type and parameter lists when defining the method in the interface.
Method declarations in interfaces have the following syntax:
public return_value nameofmethod (parameters) throws ExceptionList;
where everything in italics is optional. Also note that unlike normal method declarations in classes, declarations in interfaces are immediately followed by a semicolon.
NOTE: All methods in interfaces are public by default, regardless of the presence or absence of the public modifier. This is in contrast to class methods which default to friendly. It's actually illegal to use any of the other standard method modifiers (including native, static, synchronized, final, private, protected, or private protected) when declaring a method in an interface.
TIP: While all fields will be created as public, final, and static, you do not need to explicitly state this in the field declaration. All fields default to public, static, and final regardless of the presence of these modifiers. It is, however, a good practice to explicitly define all fields in interfaces as public, final, and static to remind yourself (and other programmers) of this fact.
public interface Product { //This variable is static and final. static final String MAKER = "My Corp"; //This variable is also static and final by default, even though not //stated explicitly. String PHONE = "555-123-4567"; public int getPrice(int id); }
Now that you know how to create interfaces, let's examine how they are used in developing classes. Listing 12.4 shows an example of a class that implements our Product interface.
class Shoe implements Product { public int getPrice(int id) { if (id == 1) return(5); else return(10); } public String getMaker() { return(MAKER); } }
Of course, the code in the class can deal with functions other than those relating to the interface (such as the getMaker() method). But, in order to fulfill the requirements of implementing the Product interface, the class must override the getPrice(int) method.
Declaring a method in an interface is a good practice. However, the method cannot be used until a class implements the interface and overrides the given method.
TIP: Remember that if you implement an interface, you are required to override all methods declared in the interface. Failure to do so will make your class abstract.
As discussed earlier, methods declared in interfaces are by default assigned the public level of access. Consequently, because you cannot override a method to be more private than it already is, all methods declared in interfaces and overridden in classes must be assigned the public access modifier, unless they are explicitly made less public in the interface.
Of the remaining modifiers that may be applied to methods, only native and abstract may be applied to methods originally declared in interfaces.
Interface methods define a set a of parameters that must be passed to the method. Consequently, declaring a new method with the same name but a different set of parameters than the method declared in your interface overloads the method, not overrides it.
While there is nothing wrong with overloading methods declared in interfaces, it is also important to implement the method declared in the interface. Therefore, unless you declare your class to be abstract, you must override each method, employing the same parameter signature as in your interface (see Listing 12.5). By the way, only one method satisfies the run() method required for Runnable.
public void Runner implements Runnable { //This method overloads the run() method;it does not //fulfill the requirements for Runnable. public void run(int max){ int count =0; while (count++<max){ try{ Thread.sleep(500); } catch (Exception e){} } } //This method fulfills the requirement for Runnable. //You must have this method. public void run(){ while (true){ try{ Thread.sleep(500); } catch (Exception e){} } } }
If the method String createName(int length, boolean capitalized) is declared in an interface, here are some valid and invalid examples of how to override it. The invalid methods can exist in addition to the valid ones, but will not be related to the interface:
Valid | Invalid | |
String createName(int a, boolean b) | String createName | (boolean capitalized, int length) |
String createName(int width, | String boolean formatted) | createName(int length) |
When creating a class that implements an interface, one of your chief concerns will be creating bodies for the methods originally declared in the interface. Unless you decide to make the method native, it is necessary to create the body for every method originally declared in your interface if you do not want to make your new class abstract.
The actual implementation and code of the body of your new method is entirely up to you. This is one of the good things about using interfaces. While the interface ensures that in a non-abstract class, its methods will be defined and will return an appropriate data type, the interface places no further restrictions or limitations on the method bodies.
You've learned how to create interfaces and build classes based on interfaces. However, interfaces are not useful unless you can develop classes that will either employ the derived classes or the interface itself.
Although the fields of an interface must be both static and final, they can be extremely useful in your code.
The following example demonstrates that any variable from an interface can be referenced by using the same dot notation you use with classes. That means you can use java.awt.image.ImageConsumer.COMPLETESCANLINES just as with the class java.awt.Event you use with java.awt.Event.MOUSE_DOWN. This provides you with access to constants. Listing 12.6 shows an example of another ImageConsumer variable being used.
class MyImageHandler { /* The java.awt.image.ImageConsumer interface defines certain constants to serve as indicators. STATICIMAGEDONE, which is set to equal 3, informs the consumer that the image is complete.*/ ImageConsumer picture; void checkStatus(boolean done) { if (done) picture.imageComplete(ImageConsumer.STATICIMAGEDONE); } }
One of the most important features of an interface is that it can be used as a
data type. An interface variable can be used just as you would any class. Output
NOTE: Notice that in treating hightop as a Product, you are implicitly
casting it as a new data type without specifically stating so in your code. While
the compiler has no trouble doing this, you could substitute that line of code in
the Store class for the following: This statement would accomplish the same goal and is often easier for other programmers
to read, because it shows that orderInfo() accepts a Product, not
a Shoe as its argument.
In order for an interface method to throw an exception, the exception type (or
one of its superclasses) must be listed in the exception list for the method as defined
in the interface. However, when dealing with interface methods, exceptions are an
exception. Here are the rules for overriding methods that throw exceptions:
In general, the exception list of the method which is declared in the interface,
not the re- declared method, determines which expectations can and cannot
be thrown. In other words, when a re-declared method changes the exception list,
it cannot add any exceptions that are not included in the original interface declaration. As an example, examine the interface and method declarations in Listing 12.9.
As a Parameter TypeListing 12.7
class Store {
static Shoe hightop;
public static void init() {
hightop = new Shoe();
}
public static void main(String argv[]) {
init();
getInfo(hightop);
orderInfo(hightop);
}
public static void getInfo(Shoe item) {
System.out.println("This Product is made by "+ item.MAKER);
System.out.println("It costs $" + item.getPrice(1) + `\n');
}
public static void orderInfo(Product item) {
System.out.println("To order from " +item.MAKER + " call " + item.PHONE + ".");
System.out.println("Each item costs $" + item.getPrice(1));
}
}
C:\dev>\jdk\java\bin\java Store
This Product is made by My Corp
It costs $5
To order from My Corp call 555-123-4567.
Each item costs $5
orderInfo( (Product)hightop);
While in this simplistic example it is not necessary to use the Product
type as your argument, its use becomes apparent when you have multiple classes, each
of which implements the same interface. For example, consider a more elaborate Store
class with several items, all of which implemented the Product interface--such
as in Listing 12.8.
Listing 12.8
interface Product {
String MAKER = "My Corp";
static final String PHONE = "555-123-4567";
public int getPrice(int id);
public void showName();
}
class Book implements Product {
public int getPrice(int id) {
if (id == 1)
return(20);
else
return(30);
}
public void showName() {
System.out.println("I'm a book!");
}
}
class Shoe implements Product {
public int getPrice(int id) {
if (id == 1)
return(5);
else
return(10);
}
public void showName() {
System.out.println("I'm a shoe!");
}
}
class store {
static Shoe hightop;
static Book using_java;
public static void init() {
hightop = new Shoe();
using_java = new Book();
}
public static void main(String argv[]) {
init();
orderInfo(hightop);
orderInfo(using_java);
}
public static void orderInfo(Product item) {
item.showName();
System.out.println("To order from " + item.MAKER + " call " + item.PHONE + ".");
System.out.println("Each item costs $" + item.getPrice(1));
}
}
Output:
C:\dev>\jdk\java\bin\java Store
I'm a shoe!
To order from My Corp call 555-123-4567.
Each item costs $5
I'm a book!
To order from My Corp call 555-123-4567.
Each item costs $20
o the constructor method.
Exceptions
Listing 12.9
interface Example {
public int getPrice(int id) throws java.lang.RuntimeException;
}
class User implements Example {
public int getPrice(int id) throws java.awt.AWTException { // Illegal - Reason 1
// java.awt.AWTException is not a subclass ofjava.lang.RuntimeException
/// method body
}
public int getPrice(int id) {
if (id == 6)
throw new java.lang.IndexOutOfBoundsException(); // Legal - Reason 2
// IndexOutOfBoundsException is derived from // RuntimeException
else
...
}
public int getPrice(int id) throws java.lang.IndexOutOfBoundsException { // Legal - Reason 1
// IndexOutOfBoundsException is derived from
//RuntimeException
if (id == 6)
throw new java.lang.ArrayIndexOutOfBoundsException(); // Legal - Reason 3
// ArrayIndexOutOfBoundsException is derived from //IndexOutOfBoundsException
...
}