Problem Derivation

   

Java™ 2 Primer Plus
By Steven Haines, Steve Potts

Table of Contents
Chapter 8.  Interfaces


In the past couple of chapters we defined two primary types of cars: Pintos and Porsches. During their creation we found that they had a lot of common attributes and behaviors, so we used the concept of inheritance to move all that common functionality to a super class named Car. Then we made the Porsche and Pinto classes extend the Car class. Thus, the Porsche and Pinto classes contained only the attributes and behaviors unique to them.

Now consider another piece of functionality that we want to add to these cars: maintainability. When these cars require service, the owners take them to a mechanic that does standard maintenance: change the oil, rotate the tires, tune up the engine, and so on. In Java we can represent this through the creation of a Mechanic class, which invokes methods on the Porsche and Pinto class instances. Tuning up a Porsche is different from tuning up a Pinto, so that functionality should reside in the individual subclasses. We'll take what we learned while building the Driver class in the previous chapter and avoid writing both tuneUpPorsche() and tuneUpPinto() methods. Instead, we'll write a single tuneUp class that knows how to tune up a Car; this is facilitated by defining the functionality abstractly to the Car super class.

Listing 8.1 shows the modifications to the Car class. Listing 8.2 shows the modifications to the Pinto class. Listing 8.3 shows the modifications to the Porsche class.

Listing 8.1 Car.java
 public abstract class Car   {      ...      public abstract void tuneUp();      public abstract void changeOil();  } 

The Car class has added two abstract methods to facilitate servicing a car: tuneUp() and changeOil(). Recall that abstract methods require all nonabstract subclasses to implement them.

Listing 8.2 Pinto.java
 public class Pinto extends Car  {      ...      public void tuneUp()      {          System.out.println( "Tuning up a pinto..." );       }      public void changeOil()      {          System.out.println( "Changing a pinto's oil..." );      }      ...  } 

The Pinto class has been modified to implement both of the new abstract Car methods in a Pinto-centric way.

Listing 8.3 Porsche.java
 public class Porsche extends Car  {      ...      public void tuneUp()      {          System.out.println( "Tuning up a porsche..." );      }       public void changeOil()      {          System.out.println( "Changing a porsche's oil..." );      }      ...  } 

The Porsche class has been modified to implement both of the new abstract Car methods in a Porsche-centric way.

Finally, Listing 8.4 shows the implementation of the first mechanic class: Mechanic1.

Listing 8.4 Mechanic1.java
 public class Mechanic1  {      public void service( Car car )      {          car.changeOil();          car.tuneUp();      }      public static void main( String[] args )      {          Porsche porsche = new Porsche();          Pinto pinto = new Pinto();          Mechanic1 mechanic = new Mechanic1();          mechanic.service( porsche );          mechanic.service( pinto );      }  } 

The Mechanic1 class defines a single method of interest:

 public void service( Car car ) 

This service() method accepts as its parameter a Car through which it calls the abstract changeOil() and tuneUp() methods.

That actually worked very well; we now have a mechanic that knows how to service cars. All future cars that we create need only extend the Car super class and implement the abstract methods, and presto, the mechanic can service them!

But now what happens when we need to service a truck or a motorcycle? Do we need a new mechanic? If a mechanic knows how to change the oil of a car, he can probably change the oil of a truck and a motorcycle, this does not fit into the inheritance hierarchy we have established. Furthermore, when you think conceptually about a car, or any vehicle, you do not think of servicing it as a property of the car itself, but as a function that the mechanic performs on the car. The car supports the capability to be serviced, but it does not perform the service itself (except maybe for some of the new self-tuning BMWs, but let's ignore those for the time being.)

The answer to these problems is that we want a mechanism external to the car itself that provides the functionality required to service a vehicle. Any vehicle that can be serviced can provide this functionality regardless of the inheritance hierarchy to which it belongs and our mechanic can service it.

Java provides the mechanism for doing this through interfaces. An interface defines a set of public methods that is implemented by all classes that want to offer a set of functionality. Then, classes that want to access that functionality can do so by referencing class instances through that interface. The first step in solving this problem is to externalize the desired functionality into an interface. An interface looks like a class, but it uses the keyword interface and all its methods must be abstract; you cannot provide any default behavior.

Listing 8.5 shows the definition for a new interface called Serviceable.

Listing 8.5 Serviceable.java
 public interface Serviceable  {      public void changeOil();      public void tuneUp();  } 

Similar to classes, interfaces are defined with a java extension. The keyword interface is used in place of class and methods do not contain any implementation. Because interfaces are abstract and all their methods are abstract by nature, you do not need to use the abstract keyword.

The Car class has been replicated to Car2 for this example and the two abstract serviceable methods, tuneUp() and changeOil(), have been removed.

Listing 8.6 shows excerpts from the new Porsche class, named Porsche2 for clarity.

Listing 8.6 Porsche2.java
 public class Porsche2 extends Car implements Serviceable  {      ...      public void tuneUp()      {          System.out.println( "Tuning up a porsche..." );      }      public void changeOil()      {          System.out.println( "Changing a porsche's oil..." );      }      ...  } 

The Porsche2 class implements the Serviceable interface by using the keyword implements:

 public class Porsche2 extends Car implements Serviceable 

A class can extend only one super class, but it can implement as many interfaces as is required. Implementing interfaces involves using the keyword, implements, following any extension followed by a comma-separated list of interfaces that it is implementing.

Implementing an interface is like signing a contract; it requires that you implement all methods defined in the interface. If you implement Serviceable, you must provide implementations for both changeOil() as well as tuneUp().

Listing 8.7 shows an excerpt from the new Pinto class: Pinto2.

Listing 8.7 Pinto2.java
 public class Pinto2 extends Car implements Serviceable  {      ...      public void tuneUp()      {          System.out.println( "Tuning up a pinto..." );      }      public void changeOil()      {          System.out.println( "Changing a pinto's oil..." );      }      ...   } 

The Pinto2 class, similar to the Porsche2 class, implements the Serviceable interface and provides implementations for both Serviceable methods: changeOil() and tuneUp().

Now that both of the cars implement a common Serviceable interface, we can define a mechanic that can service them. Listing 8.8, called Mechanic2, defines a new mechanic.

Listing 8.8 Mechanic2.java
 public class Mechanic2  {      public void service( Serviceable s )      {          s.changeOil();          s.tuneUp();      }       public static void main( String[] args )      {          Porsche porsche = new Porsche();          Pinto pinto = new Pinto();          Mechanic1 mechanic = new Mechanic1();          mechanic.service( porsche );          mechanic.service( pinto );      }  } 

Like Mechanic1, Mechanic2 defines a single method service. Instead of working Car classes and their derivatives, it works on classes implementing the Serviceable interface. Thus, its signature is as follows:

 public void service( Serviceable s ) 

Thus, you can create an instance of a class that implements Serviceable and pass it to the mechanic's service method. That is precisely what is done in the mechanic's main method: A Porsche2 and a Pinto2 are created and passed to the Mechanic2 class's service method.

When you define a method that accepts an interface, you are limited to calling only those methods that are defined in the interface; you cannot make calls to other methods implemented by the class. For example, you can call the changeOil and tuneUp and methods of the Porsche2 class, but you cannot call its engageTurbos method.


       
    Top
     



    Java 2 Primer Plus
    Java 2 Primer Plus
    ISBN: 0672324156
    EAN: 2147483647
    Year: 2001
    Pages: 332

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net