Chapter 11. Interfaces

  • What Problem Does an Interface Solve?

  • Interface java.lang.Comparable

  • Interfaces Versus Abstract Classes

  • Granting Permission Through an Interface—Cloneable

  • What Protected Really Means

  • Using Interface Callbacks for GUI Event Handlers

  • The Class Double

  • Exercises

  • Some Light Relief—The Java-Powered Toaster

Interfaces are an important concept in Java. In this chapter, we'll explain what interfaces do, and how you use them. Here's a summary of what interfaces do, so you can see where we're headed.

In a few words, an interface is similar to a class that only abstract methods. You can't instantiate an abstract class; its purpose is to force any subclasses to implement its methods. That allows other classes to rely on those methods being present in the subclasses.

An interface does the same job as a class, but without the requirement that there is a parent/child relationship. Interfaces are reference types, like classes and arrays. Interface types are used as parameters, and an actual class with that behavior will be used as an argument. A class associates itself with an interface by saying it implements an interface, thereby promising that it provides all the methods that the interface specifies.

Let's start by describing the problem that interfaces solve.

What Problem Does an Interface Solve?

We've seen in previous chapters how classes can be related in a hierarchy, with the common behavior higher up the tree. An example is given in Figure 11-1.

Figure 11-1. Where should the refuel() method go?

image

Figure 11-1 shows some classes relating to means of transport. Each box is a class, labeled with the class name, and with an example method that belongs in that class. The parent class is called “Transport” and has a method called buy(). Whatever kind of vehicle you have, you have to buy it before it becomes yours. Putting the method in the parent class means that all subclasses inherit it.

Transport has three child classes. The class called “Car” has a method called drive(), and this is inherited by its two subclasses, Convertible and Saloon. Whatever kind of car it is, the way you move it is to drive(). It belongs in Car, not Transport, because you ride a bicycle or fly an airplane, not drive them.

Now let's imagine we want to keep refuelling information in this class hierarchy. We want to add a method called refuel() that fills the fuel tank of each vehicle. The class that keeps track of our supply depot will call the refuel method on vehicle objects. Where is the right place to add refuel() in the tree of classes?

We cannot add a refuel() method in the transport class, because Bicycle would inherit it, and bicycles are not refuelled. We can add the method individually to Airplane and Car and any other unrelated Classes, like Generator, which represent something you can refuel.

That's good, but it causes problems for our supply depot class. The supply depot class will call an object's refuel() method. What should be the type of the things we pass to it for refuelling? We cannot pass a Transport, because not all transport objects have a refuel() method, and some non-Transport things (like Generator or Pump) need refuelling.

We could overload service() with one version for each type that has a refuel method.

public class SupplyDepot {
     public void service(Airplane a)  { /* more code */ }
     public void service(Car c)       { /* more code */ }
     public void service(Generator g) { /* more code */ }
     public void service(Pump p)      { /* more code */ }
         // more methods, omitted
}

But that's not very convenient. If we come along with some new thing that is capable of being refuelled, like a JetSki, we now have to modify the SupplyDepot class to add a service (JetSki j) method. Well designed systems don't cause changes to ripple across classes this way.

We are looking for a way to say “I represent any type that has a refuel() method” (see Figure 11-2). Then we just make the argument to service() be that type. Some OOP languages allow classes to have more than one parent, a feature known as “multiple inheritance.” Multiple inheritance would solve this problem by having a class called CapableOfBeingRefuelled with a single method called refuel(). Everything that can be refuelled will be where it is now in the class hierarchy, but it will also have CapableOfBeingRefuelled as a second parent.

Figure 11-2. What type should thingToService be to represent everything that has a refuel() method?

image

When a language has multiple inheritance, it often proves to be a controversial feature because it seems to need a great many confusing and unintuitive language rules. Java does not have a multiple inheritance feature, but it has something that fills a similar role in a simpler way. Step forward, interfaces!

Interfaces are in the Java language to allow a class to say it has some particular behavior. An interface type acts as a placeholder for real classes in any place where that behavior is expected.

We already saw something similar with inheritance, but you can inherit only from one class. A class can implement any number of interfaces. Interfaces say, “I offer these methods, and you can later use any object from a class that implements me in any place you are using me.” Interfaces provide the functionality of multiple inheritance, without the difficulties.

Remember some of the sample Java classes, like java.lang.String and java.lang.Integer we have presented, saying “here is the API” and showing a list of the method names (but not the code inside the methods)? The “I” in API even stands for “Interface” (Application Programmer Interface).

This is what a Java interface is: A class-like item that has a list of method names, not the method bodies. For this reason, some people say that “Java has multiple inheritance of interface, but not multiple inheritance of implementation.” An interface is a skeleton of a class showing the methods the class will have when someone implements it. An interface looks like the following:

public interface CapableOfBeingRefuelled {
    public int refuel();
}

An interface looks a bit like a class (actually, with no bodies, it's more like an abstract class), is counted as a reference type, and can generally be used in most of the same places as a class. It doesn't have any behavior, though—it's just a description of promised behavior. Here's how the members behave, regardless of whether you supply these modifiers:

  • The members of an interface—the fields and methods—are public, even if you don't label them so.

  • Any data fields in an interface are final, even if you don't label them so.

  • The methods in an interface are abstract, even if you don't label them so.

Any class that has a refuel method should say that it implements the CapableOfBeingRefuelled interface, as follows:

public class Airplane implements CapableOfBeingRefuelled {
     public int refuel() {
          purgeTanks();
          leftTank = this.capacity;
          rightTank = this.capacity;
     }
        // more methods, omitted
}

Now here comes the clever part. The SupplyDepot's service method will take an argument that is type CapableOfBeingRefuelled! Class Airplane, Generator, JetSki, Car and so on all have that type by virtue of implementing that interface. Therefore, they can all be passed to the service method. SupplyDepot will look like the following.

public class SupplyDepot {
     public void service(CapableOfBeingRefuelled thingToService) {
          thingToService.refuel();
     }
          // more methods, omitted
}

(To make this completely functional we'd need to provide parameters that tell the supply depot the capacity of the item to refuel, and decrement that amount of fuel from the depot).

When we come along with something additional that needs to be refuelled, like a snowmobile, no change is needed in the SupplyDepot class. The snowmobile is declared as follows, and it is therefore already compatible with the argument to SupplyDepot's service() method:

public class Snowmobile implements CapableOfBeingRefuelled {
     public int refuel() {
               ...
     }

     ... // more methods, omitted
}

The fundamental reason for interfaces is to allow classes to say, in a way the compiler can check on it, “I have the behavior X, and you can use one of my objects wherever you have something that needs to do X”.

In summary:

  • Use the interface type as a parameter for a method M. Inside that method M, you can call any of the methods promised by the interface parameter. When you actually call the method M, you will have to provide an object that implements the interface and thus has all the methods promised by the interface.

  • Interfaces let you compile your code now. But you need to provide an object with actual code for the thing to work at run-time.

  • The interface specifies the exact signatures of the methods that must be provided by the implementing classes.

A class can extend only one superclass, but it can implement any number of interfaces. Two interfaces can make the same demand for a method of a given name in a class without. For example, interface A specifies a refuel() method, interface B specifies a refuel() method, and class C implements both interfaces.

This is one of the situations that causes teeth grinding among the multiple inheritance fans. In C++ that class is going to inherit two different refuel methods with the same signature. It doesn't cause any problem in Java. In Java, class C implements one refuel() method and it satisfies both interfaces.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.141.27