Incremental Java
Interfaces

Looking Back

When we started looking at classes, we introduced methods, and in particular, method signatures. As you've seen by now, class definitions really don't have method signatures (which include return type, method name, and a parameter list). They have more. They have method definitions (which includes the method signature, plus the method body).

So we didn't say the complete truth about classes when we said they contained method signatures. It made it easier to explain classes by going through a two step process.

However, it turns out, there are entities like classes which do have method signatures. They're called interfaces.

Interfaces

Interfaces are merely a list of methods. They have no instance variables. They have no method bodies. You can't create an object from an interface, for this reason.

So what's the point? Why have interfaces?

Suppose you have some drawing program, and for some reason, you need all shapes to be able to support a getArea() method and a getPerimeter() method. You don't particularly care what kind of classes you have. In fact, you might have a Triangle class, a Rectangle class, and a Polygon class which all have these methods.

How could you work with objects of any of these types, if you want to use getArea() and getPerimeter().

Example

Let's look at an example of an interface. Let's call it Drawable.
public interface Drawable 
{
   public int getArea() ;
   public int getPerimeter() ;
}
This should go in a file called Drawable.java.

We put public in front of the method signatures. It doesn't really make sense to put private. private is meant for internal use, and class designers should be allowed to pick private methods.

Interfaces are meant for public methods from the perspective of the object user.

Think of it as a list of requirements that a class must have. Let's see an example of this.

public class Rectangle implements Drawable 
{
   // CODE GOES HERE
}
Notice that we added implements Drawable at the end of the public class Rectangle.

This means that the class is attempting to implement all methods listed in the Drawable interface. When Java compile the code, it looks at the interface, and sees if all methods listed in there are listed in your class. If it's not, then there's a compiler error.

Using Interfaces

So what? Now we know Rectangle has methods as specified by Drawable. Does it matter?

Let's say, for example, that we have Ellipse, Triangle, and Trapezoid. Each of them also implements the Drawable interface.

The real magic is this: we can treat Drawable like a type. Let's see how.

public class FooTest
{
   public static void main( String [] args )
   {
      Drawable d1 = new Rectangle( 2, 3 ) ;
      Drawable d2 = new Triangle( 4, 5 ) ;
      Drawable d3 = new Ellipse( 3, 8 ) ;
      Drawable d4 = new Trapezoid( 3, 8, 10 ) ;

      System.out.println( "Rect area: " + d1.getArea() ) ;
      System.out.println( "Triangle area: " + d2.getArea() ) ;
      System.out.println( "Ellipse area: " + d3.getArea() ) ;
      System.out.println( "Trapezoid area: " + d4.getArea() ) ;
   }
}
Let's look at the first unusual statement.
Drawable d1 = new Rectangle( 2, 3 ) ;
We're assigning a Rectangle object handle to a Drawable variable. It is not a Drawable object, because Drawable is not a class. It's an interface.

Yet Java permits this. You can assign an object to an interface variable provided the type of the object implements the interface. In this case, since Rectangle implements Drawable we can make this assignment.

The Java compiler checks to make sure each assignment is valid by checking to see if each object's type implements the Drawable interface.

Although d1 has a handle to a Rectangle object, d1 has a declared type of Drawable. If you use d1, you can only use Drawable methods. For example, you can no longer directly use getWidth() or getHeight().

The rule of thumb: you can only use interface methods with an interface variable.

Notice how powerful interfaces are. We can get the area of many different types and treat them as if they were from the same class, even though they aren't.

Dynamic Casting

Suppose we knew d1, a Drawable variable, happened to have a handle to a Rectangle object. We can't use Rectangle methods with d1. But what if we really needed to?

We can do an operation called dynamic casting.

Drawable d1 = new Rectangle( 2, 3 ) ;

Rectangle rect = (Rectangle) d1 ;
(Rectangle) is a dynamic cast. You put parentheses around the class. While the program is running, the JVM looks at the actual type of d1. The JVM checks to see if d1 holds a handle to a Rectangle object. If it does, the cast succeeds, and we can use rect, which now has a handle to the same Rectangle object, but we can now call all the methods of Rectangle.

The cast could fail. Perhaps d1 has a handle to a Triangle object. In that case, when the JVM is checking to see whether d1 has a handle to a Rectangle, it fails.

This creates an error, and typically causes your program to end. This error is called an exception. There are ways to deal with the error. However, we'll talk about that later on when we explain how exceptions work.

Interfaces are Good

There's now a belief in the programming community where we should use interfaces whenever possible. If we use interfaces, then we can create many different classes that implement the interface in slightly different ways (such as the Drawable interface).

This allows us flexibility later on. If we want to change the behavior (say, use an Ellipse instead of a Rectangle), then we write a class that implements the interface, create an interface variable, and as long as most of the code uses the interface variable, we're set. We have to make no new changes.

Just to give you an idea of the power of the idea, imagine you've written a class and you've used a lot of Rectangle objects. Say, it appears 1000 times. But now you want to use Ellipse instead. Now you have to search and replace 1000 times.

If we had used Drawable throughout (assuming that only getArea() and getPerimeter() are the only methods used), then we would have only to initialize the Drawable variable to an Ellipse object, and the change would be very simple.

Interfaces As a List of Requirements

Think of an interface as a list of requirements, where the requirements are methods. You can then create classes that implement this interface if the class implement all methods in the interface.

Implementing Multiple Interfaces

You can implement more than one interface. For example, suppose Rectangle implements Drawable and Comparable, both of which are interfaces.

You write the header of the class definition as:

public class Rectangle implements Drawable, Comparable
{
}
You must implement all methods in both these interfaces. You can then declare either a Drawable variable or a Comparable variable and assign it to a Rectangle object.