InterfacesHomepage  « Learn Java5 « Interfaces

This lesson looks at interfaces and the contracts we provide when using them. What does the last sentence actually mean? To explore this we will add a new class to our Vehicle hierarchy and take the explanation forwards from there:

interface 1 interface 2 interface 3 interface 4 interface 5

When we last visited Vehicle Town, we had made the Vehicle and Truck classes abstract to stop them being instantiated. We also made the load() method abstract in the Truck class to ensure the first concrete subclass had to implement it. Meanwhile back in Vehicle town the amount of people is putting a strain on our bus network and the budget. The mayor wants us to bring in taxis and make people queue and pay for buses and taxis.

We could introduce a new Taxi class with a couple of new methods that we also want to add to the Bus class.

We could introduce a new Taxi class with a couple of new methods that we put into the Vehicle superclass.

We could introduce a new Taxi class with a couple of new abstract methods that we put into the Vehicle superclass.

We decide to create a new interface called PublicTransport with the new queue() and payFare() methods and implement the interface in the appropriate classes.

Press the button below to cycle through our Vehicle Town slideshow.



/*
  Some code showing class rules
*/
class A { }                        // OK
private class A { }                // No, can only use public access modifier with top level class
protected class A { }              // No, can only use public access modifier with top level class
public class A { }                 // OK
public class B extends A { }       // OK
public class C extends B, A { }    // No, can only extend one class
abstract class D { }               // OK
final class E { }                  // OK
abstract final class F { }    	   // No, cannot use both abstract and final

Using the interface keywordgo to top of page Top

So, we have now decided to use an interface to put our new queue() and payFare() methods in, we can talk about and then code up the interface for this purpose. We create an interface using the interface keyword, instead of class in the definition. All interface methods are implicitly declared as public and abstract so there is no need to code these modifiers when creating the interface. Just like the abstract methods we saw earlier in the section, interface methods have no body and end in a semicolon. In fact you can think of an interface as a pure abstract class because every method must be abstract. Ok, Lets code up our new PublicTransport interface:


/*
 The PublicTransport interface
*/ 
public interface PublicTransport {

    void queue(int queue);  // The public and abstract modifiers are added by the compiler
    void payFare(int money);  // The public and abstract modifiers are added by the compiler
}

Save and compile and the file in directory   c:\_OOConcepts in the usual way.

Using the implements keywordgo to top of page Top

Now we have coded up our new PublicTransport interface to use it in a class we use the implements keyword in the classes we want to implement our interface in. We will start with our existing Bus class, just add implements PublicTransport to the class declaration:


/*
 A Bus Class
*/ 
public class Bus extends Vehicle implements PublicTransport {

    ... 
}

Save and compile the file in directory   c:\_OOConcepts in the usual way. The implements keyword must follow the extends keyword, if it is present, or you will get a compiler error.

run Bus class implementation

The above screenshot shows the output of running our updated Bus class. We get a compiler error because we haven't implemented the methods from the PublicTransport interface. Just like with abstract classes we need to honour the contract by implementing all abstract methods in the first concrete subclass:


public class Bus extends Vehicle implements PublicTransport {
    /*
      Our queue() implementation
    */ 
    void queue(int people) {
        System.out.println("There are " + people + " people waiting for a bus.");
        
    }
    /*
      Our payFare() implementation
    */ 
    void payFare(int fare) {
        System.out.println("This bus journey will cost you " + fare + " pounds.");
        
    }
    ... 
}

Save and compile the file in directory   c:\_OOConcepts in the usual way.

run Bus class implementation

The above screenshot shows the output of running our updated Bus class. We forgot to give our implementations the public access modifier and absract implementations can't be more restrictive. If you remember from the lesson on abstraction, implementing an abstract method is, it's the same as overriding a method from a concrete superclass, therefore the same rules apply as those for overriding method rules. Add the public access modifier to the front of our new methods and recompile the class. Following is a test class to make sure everything still works in our Bus class:


/*
 Test class for Bus
*/ 
public class TestBus {

    public static void main (String[] args) {
        Bus a1 = new Bus();
        a1.setChassis("4-axle chassis");
        a1.setMotor("12 stroke");
        a1.setWheels(8);
        a1.setConductor(true);
        System.out.println("Our bus has a " + a1.getChassis() + ", " + a1.getMotor() 
                                            + " motor and has " + a1.getWheels() + " wheels.");
        if (a1.getConductor()) {
            System.out.println("This bus has a driver and a conductor."); 
        } else {
            System.out.println("This bus has a driver only."); 
        }                                    
        a1.service(3); 
        a1.carry(60); 
        a1.load("people."); 
        a1.queue(12); 
        a1.payFare(2); 
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run  Bus class implementation

The above screenshot shows the output of running our TestBus test class, everything works fine. We will code up our Taxi class as well for a bit of practice and for use in the next part of the lesson:


public class Taxi extends Vehicle implements PublicTransport {
    /*
      Our queue() implementation
    */ 
    public void queue(int people) {
        System.out.println("There are " + people + " people waiting at the taxi rank.");
        
    }
    /*
      Our payFare() implementation
    */ 
    public void payFare(int fare) {
        System.out.println("This taxi ride will cost you " + fare + " pounds.");
        
    }
}

Save and compile the file in directory   c:\_OOConcepts in the usual way.

Implementing Multiple Interfacesgo to top of page Top

It is also perfectly legal to implement multiple interfaces and the following code shows the syntax for doing this:


public class Taxi extends Vehicle implements PublicTransport, Serializable, Runnable {
    ...    
}

Deeper Into Polymorphismgo to top of page Top

So we have found a way to get around multiple inheritance by using interfaces but what are we really getting from this? The interfaces don't contain any implementation code as they are purely abstract and we have to implement them in the first concrete subclass. The answer is polymorphism! We can pass a polymorphic interface type as an argument and return a polymorphic interface type as well. A slideshow is in order to set up the scenario:

interface 6 interface 7 interface 8

Press the button below to cycle through our Vehicle Town slideshow.


We are going to make the Plane superclass and PassengerPlane subclass nice and simple and we won't bother with the MilitaryPlane code as it doesn't add to the objective:


/*
 A Plane class
*/ 
public class Plane {

}
/*
 A PassengerPlane class
*/ 
public class PassengerPlane extends Plane implements PublicTransport { 
    /*
      Our queue() implementation
    */
    public void queue(int people) {
        System.out.println("There are " + people + " people waiting to board this plane.");
        
    } 
    /*
      Our payFare() implementation
    */
    public void payFare(int fare) {
        System.out.println("This plane journey will cost you " + fare + " pounds.");
        
    }
}
/*
 Test class for PassengerPlane
*/ 
public class TestPassengerPlane {

    public static void main (String[] args) {
        PassengerPlane plane = new PassengerPlane();
        plane.queue(152); 
        plane.payFare(502); 
    }
}

Save and compile the Plane, PassengerPlane and TestPassengerPlane classes and then run the TestPassengerPlane class in directory   c:\_OOConcepts in the usual way.

run PassengerPlane Test

The above screenshot shows the output of running our TestPassengerPlane class, as you can see it's very easy to implement interfaces in multiple hierarchies.

Polymorphic Interface Argumentsgo to top of page Top

Ok, we have seen how easy it is to implement interfaces in multiple hierarchies but what about the polymorphism? Well because the implementations can come from different hierarchies we can use the interface type as the reference type when creating our objects. As we know, an implemented abstract type is exactly the same as an overridden method type and so will use polymorphic method invocation to dynamically invoke the method of the the actual object type at runtime. This means we can group together objects from different hierarchies under the umbrella of the interface type as the reference type knowing the correct object will be always be invoked at runtime. Now we are ready to see polymorphic interface arguments in action and for this we need a couple of new classes:


/*
 A Carrier class
*/ 
public class Carrier {
    private int people;
    private int cost;

    Carrier(int people, int cost) {
        this.people = people;
        this.cost = cost;
    }

    public void travelInfo (PublicTransport pt) {
        pt.queue(people); 
        pt.payFare(cost); 
    }
}
/*
 Test class for polymorphic interface arguments
*/ 
public class TestPolyIntArgs {

    public static void main (String[] args) {
        PublicTransport bus = new Bus();  
        PublicTransport taxi = new Taxi();  
        PublicTransport pp = new PassengerPlane();  
        Carrier carrier = new Carrier(10, 2); 
        carrier.travelInfo(bus);
        Carrier carrier2 = new Carrier(4, 8); 
        carrier2.travelInfo(taxi);
        Carrier carrier3 = new Carrier(162, 720); 
        carrier3.travelInfo(pp);
    }
}

In the code above for the Carrier class we are using the PublicTransport interface type in our parameter lists for the methods in the class. In the TestPolyIntArgs test class we are passing across the argument types polymorphically using the PublicTransport reference type. Save and compile the Carrier and TestPolyIntArgs classes and then run the TestPolyIntArgs class in directory   c:\_OOConcepts in the usual way.

run PassengerPlane Test

The above screenshot shows the output of running our TestPolyIntArgs class. We are passing across objects from totally different hierarchies using the PublicTransport interface type as the reference type. The JVM uses polymorphic method invocation to dynamically invoke the correct queue() and payFare() methods based on the actual object type within the Carrier class methods at runtime.

Polymorphic Interface Return Typesgo to top of page Top

We can also return objects from methods using the interface type as the reference type, safe in the knowledge that the correct method will be invoked on the actual object at runtime.


/*
 Test class for Polymorphic Interface Type Return
*/ 
public class PolymorphicIntReturn {

    public static void main (String[] args) {
        // Array declaration below is creating an Array object so can use interface supertype
        PublicTransport[] pt = new PublicTransport[3]; 
        for (int i=0; i < 3; i++) {
            pt[i] = createPaidTransport(i);
            pt[i].queue(i);  
            pt[i].payFare(i);  
        } 
    }
    /*
      Create a subtype from the Vehicle or Plane hierarchy and
      return the PublicTransport supertype
    */ 
    static PublicTransport createPaidTransport (int i) {
    PublicTransport pt;
	switch (i) {
        case 0:
            pt = new Bus();
            break;
        case 1:
            pt = new Taxi();
            break;
        default:
            pt = new PassengerPlane();
    }
        return pt;
    }
}

In the code above we are instantiating objects from totally different hierachies using the PublicTransport interface type as the reference type. Save, compile and run the PolymorphicIntReturn in directory   c:\_OOConcepts in the usual way.

run Polymorphic Array

The above screenshot shows the output of running our slightly contrived PolymorphicIntReturn test class. We are returning the PublicTransport interface type from the createPaidTransport() method. The JVM uses polymorphic method invocation to dynamically invoke the correct queue() and payFare() methods based on the actual object type on return.

For more on polymorphism and how it works take a look at the Polymorphism lesson.

Using instanceof With Interfacesgo to top of page Top

We first looked at the instanceof operator in the Reference Variables lesson. Now we have learnt about polymorphic types and interfaces it's time to take another look at this operator and its application. The following program uses the Vehicle hierarchy and the PublicTransport interface to show some instanceof usages:


/*
  A Minibus Class
*/ 
public class Minibus extends Bus {
    public static void main(String[] args) {
        Vehicle bus = new Bus();
        checkType(bus);
        Vehicle car = new Car();
        checkType(car);
        Vehicle minibus = new Minibus();
        checkType(minibus);
        PassengerPlane  pp = new PassengerPlane();
        checkType(pp);
        Vehicle taxi = new Taxi();
        checkType(taxi);
    }
    public static void checkType(Object o) {
        if (o instanceof Minibus) {
            ((Minibus)o).MinibusClassOnlyMethod(); // Downcast to minibus
        }
        if (o instanceof PublicTransport ) {
            /*
              Downcast argument to PublicTransport interface type
            */                
            PublicTransportOnlyMethod(((PublicTransport)o));  
        }
    } 
    private static void MinibusClassOnlyMethod() {
        System.out.println("We are in the MinibusClassOnlyMethod()."); 
        System.out.println("We have safely downcast to the Minibus class to get here."); 
    } 
    private static void PublicTransportOnlyMethod(PublicTransport pt) {
        pt.queue(10); 
        pt.payFare(2); 
    } 
}

In the code above we are instantiating objects from two different hierachies. We call the checkType(Object o) which uses The Object Superclass as a parameter. This enables us to pass any argument to the method. Within the method we firstly use the instanceof operator to test for a Minubus type and if so we print some messages for that type. We then check to see if this type is an instance of the PublicTransport interface. If it is we cast the object argument to the PublicTransport interface type and pass this to the PublicTransportOnlyMethod(PublicTransport pt) method. Within this method the overridden methods for the appropriate class are called. Save, compile and run the Minibus in directory   c:\_OOConcepts in the usual way.

run Minibus Class

The above screenshot shows the output of running our Minibus test class. If you look closely at the output you will see that the queue() and payFare() methods are output twice for the Bus class and yet we only made one occurrence of Bus. The second lot of output is actually for the Minibus class. The reason for this is inheritance; if a superclass implements an interface, then a subclass will have an implicit implementation through the superclass.

Interfaces Quizgo to top of page Top

Try the quiz below to test your knowledge of this lesson

Question 1 : How do we use an interface in our classes?
- We use an interface in our classes by implementing it with the <code>implements</code> keyword in the class definition.
Question 2 : Two modifiers are implicitly applied to methods within an interface, what are they?
- The <code>public</code> and <code>abstract</code> modifiers are implicitly applied to methods within an interface.
Question 3 : Is the following code snippet valid?

public class B implements C extends A { }
- This code snippet is invalid as the <code>implements</code> keyword must follow the <code>extends</code> keyword when used together.
Question 4 : What is the main benefit of interfaces?
- Interfaces allow us to use method arguments and return types polymorphically.
Question 5 : Assuming C, D and E are interfaces is the following code snippet valid?

public class B extends A implements C, D, E { }
- This snippet is perfectly valid as we can implement any number of interfaces.
Question 6 : Assuming the following are in different files is the following code snippet valid?

public interface A { void b(int b); }
public class B implements A { void b(int b) {} }
- Class <code>B</code> won't compile as the <code>b()</code> method is less restrictive. <br />. The complier implicitly applies the <code>public</code> and <code>abstract</code> modifiers to interface methods and <em>overrides</em> methods can't be less restrictive.
Question 7 : What would be the output of the following code snippet?

public interface A { void b(); }
public class B implements A { public void b() {} }
public class C extends B {
public static void main(String[] args) {
B b = new B();
C c = new C();
if (b instanceof A) { System.out.print("true,"); }
if (c instanceof B) { System.out.print("true,"); }
if (c instanceof A) { System.out.print("true"); }
}
}
- The output will be 'true,true,true' as class <code>C</code> inherits the implementation of interface <code>A</code> through class <code>B</code>
Question 8 : What must we ensure when implementing an interface?
- We must honour the interface contract by ensuring we <em>override</em> all methods in the implementation class or first <em>concrete subclass</em> thereof.
Status Bar Please select an answer

What's Next?

In the next lesson we look at the various types of nested classes.

<<  Polymorphism                    Nested Classes  >>

go to home page Java5 Tutor Homepage go to top of page Top